patchwork-os 0.2.0-alpha.2 → 0.2.0-alpha.22

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 (281) hide show
  1. package/README.bridge.md +6 -0
  2. package/README.md +40 -15
  3. package/deploy/bootstrap-vps.sh +184 -0
  4. package/dist/approvalHttp.d.ts +11 -2
  5. package/dist/approvalHttp.js +98 -10
  6. package/dist/approvalHttp.js.map +1 -1
  7. package/dist/approvalQueue.d.ts +12 -1
  8. package/dist/approvalQueue.js +25 -3
  9. package/dist/approvalQueue.js.map +1 -1
  10. package/dist/automation.d.ts +20 -0
  11. package/dist/automation.js +35 -0
  12. package/dist/automation.js.map +1 -1
  13. package/dist/bridge.js +145 -23
  14. package/dist/bridge.js.map +1 -1
  15. package/dist/bridgeToken.js +57 -19
  16. package/dist/bridgeToken.js.map +1 -1
  17. package/dist/claudeDriver.d.ts +3 -1
  18. package/dist/claudeDriver.js +48 -0
  19. package/dist/claudeDriver.js.map +1 -1
  20. package/dist/claudeOrchestrator.d.ts +1 -1
  21. package/dist/claudeOrchestrator.js +14 -8
  22. package/dist/claudeOrchestrator.js.map +1 -1
  23. package/dist/commands/launchd.d.ts +2 -0
  24. package/dist/commands/launchd.js +94 -0
  25. package/dist/commands/launchd.js.map +1 -0
  26. package/dist/commands/recipe.d.ts +256 -0
  27. package/dist/commands/recipe.js +1313 -0
  28. package/dist/commands/recipe.js.map +1 -0
  29. package/dist/config.d.ts +15 -2
  30. package/dist/config.js +94 -8
  31. package/dist/config.js.map +1 -1
  32. package/dist/connectors/baseConnector.d.ts +117 -0
  33. package/dist/connectors/baseConnector.js +213 -0
  34. package/dist/connectors/baseConnector.js.map +1 -0
  35. package/dist/connectors/confluence.d.ts +111 -0
  36. package/dist/connectors/confluence.js +406 -0
  37. package/dist/connectors/confluence.js.map +1 -0
  38. package/dist/connectors/fixtureLibrary.d.ts +21 -0
  39. package/dist/connectors/fixtureLibrary.js +70 -0
  40. package/dist/connectors/fixtureLibrary.js.map +1 -0
  41. package/dist/connectors/fixtureRecorder.d.ts +1 -0
  42. package/dist/connectors/fixtureRecorder.js +35 -0
  43. package/dist/connectors/fixtureRecorder.js.map +1 -0
  44. package/dist/connectors/github.d.ts +58 -8
  45. package/dist/connectors/github.js +312 -84
  46. package/dist/connectors/github.js.map +1 -1
  47. package/dist/connectors/gmail.d.ts +4 -1
  48. package/dist/connectors/gmail.js +93 -16
  49. package/dist/connectors/gmail.js.map +1 -1
  50. package/dist/connectors/googleCalendar.d.ts +60 -0
  51. package/dist/connectors/googleCalendar.js +345 -0
  52. package/dist/connectors/googleCalendar.js.map +1 -0
  53. package/dist/connectors/jira.d.ts +98 -0
  54. package/dist/connectors/jira.js +379 -0
  55. package/dist/connectors/jira.js.map +1 -0
  56. package/dist/connectors/linear.d.ts +117 -0
  57. package/dist/connectors/linear.js +239 -0
  58. package/dist/connectors/linear.js.map +1 -0
  59. package/dist/connectors/mcpClient.d.ts +56 -0
  60. package/dist/connectors/mcpClient.js +189 -0
  61. package/dist/connectors/mcpClient.js.map +1 -0
  62. package/dist/connectors/mcpOAuth.d.ts +84 -0
  63. package/dist/connectors/mcpOAuth.js +389 -0
  64. package/dist/connectors/mcpOAuth.js.map +1 -0
  65. package/dist/connectors/mockConnector.d.ts +28 -0
  66. package/dist/connectors/mockConnector.js +81 -0
  67. package/dist/connectors/mockConnector.js.map +1 -0
  68. package/dist/connectors/notion.d.ts +143 -0
  69. package/dist/connectors/notion.js +424 -0
  70. package/dist/connectors/notion.js.map +1 -0
  71. package/dist/connectors/sentry.d.ts +43 -0
  72. package/dist/connectors/sentry.js +188 -0
  73. package/dist/connectors/sentry.js.map +1 -0
  74. package/dist/connectors/slack.d.ts +50 -0
  75. package/dist/connectors/slack.js +324 -0
  76. package/dist/connectors/slack.js.map +1 -0
  77. package/dist/connectors/tokenStorage.d.ts +35 -0
  78. package/dist/connectors/tokenStorage.js +394 -0
  79. package/dist/connectors/tokenStorage.js.map +1 -0
  80. package/dist/connectors/zendesk.d.ts +104 -0
  81. package/dist/connectors/zendesk.js +424 -0
  82. package/dist/connectors/zendesk.js.map +1 -0
  83. package/dist/drivers/claude/api.d.ts +11 -0
  84. package/dist/drivers/claude/api.js +54 -0
  85. package/dist/drivers/claude/api.js.map +1 -0
  86. package/dist/drivers/claude/envSanitizer.d.ts +7 -0
  87. package/dist/drivers/claude/envSanitizer.js +18 -0
  88. package/dist/drivers/claude/envSanitizer.js.map +1 -0
  89. package/dist/drivers/claude/streamParser.d.ts +38 -0
  90. package/dist/drivers/claude/streamParser.js +34 -0
  91. package/dist/drivers/claude/streamParser.js.map +1 -0
  92. package/dist/drivers/claude/subprocess.d.ts +19 -0
  93. package/dist/drivers/claude/subprocess.js +216 -0
  94. package/dist/drivers/claude/subprocess.js.map +1 -0
  95. package/dist/drivers/claude/subprocessSettings.d.ts +9 -0
  96. package/dist/drivers/claude/subprocessSettings.js +55 -0
  97. package/dist/drivers/claude/subprocessSettings.js.map +1 -0
  98. package/dist/drivers/gemini/index.d.ts +18 -0
  99. package/dist/drivers/gemini/index.js +210 -0
  100. package/dist/drivers/gemini/index.js.map +1 -0
  101. package/dist/drivers/grok/index.d.ts +11 -0
  102. package/dist/drivers/grok/index.js +22 -0
  103. package/dist/drivers/grok/index.js.map +1 -0
  104. package/dist/drivers/index.d.ts +23 -0
  105. package/dist/drivers/index.js +31 -0
  106. package/dist/drivers/index.js.map +1 -0
  107. package/dist/drivers/openai/index.d.ts +24 -0
  108. package/dist/drivers/openai/index.js +110 -0
  109. package/dist/drivers/openai/index.js.map +1 -0
  110. package/dist/drivers/types.d.ts +72 -0
  111. package/dist/drivers/types.js +30 -0
  112. package/dist/drivers/types.js.map +1 -0
  113. package/dist/featureFlags.d.ts +73 -0
  114. package/dist/featureFlags.js +203 -0
  115. package/dist/featureFlags.js.map +1 -0
  116. package/dist/fp/automationInterpreter.js +1 -0
  117. package/dist/fp/automationInterpreter.js.map +1 -1
  118. package/dist/fp/automationProgram.d.ts +1 -1
  119. package/dist/fp/automationProgram.js.map +1 -1
  120. package/dist/fp/policyParser.js +17 -0
  121. package/dist/fp/policyParser.js.map +1 -1
  122. package/dist/index.js +543 -37
  123. package/dist/index.js.map +1 -1
  124. package/dist/installGuard.d.ts +25 -0
  125. package/dist/installGuard.js +48 -0
  126. package/dist/installGuard.js.map +1 -0
  127. package/dist/oauth.d.ts +4 -1
  128. package/dist/oauth.js +50 -14
  129. package/dist/oauth.js.map +1 -1
  130. package/dist/patchworkConfig.d.ts +9 -0
  131. package/dist/patchworkConfig.js.map +1 -1
  132. package/dist/recipes/chainedRunner.d.ts +104 -0
  133. package/dist/recipes/chainedRunner.js +359 -0
  134. package/dist/recipes/chainedRunner.js.map +1 -0
  135. package/dist/recipes/dependencyGraph.d.ts +39 -0
  136. package/dist/recipes/dependencyGraph.js +199 -0
  137. package/dist/recipes/dependencyGraph.js.map +1 -0
  138. package/dist/recipes/legacyRecipeCompat.d.ts +1 -0
  139. package/dist/recipes/legacyRecipeCompat.js +97 -0
  140. package/dist/recipes/legacyRecipeCompat.js.map +1 -0
  141. package/dist/recipes/nestedRecipeStep.d.ts +58 -0
  142. package/dist/recipes/nestedRecipeStep.js +95 -0
  143. package/dist/recipes/nestedRecipeStep.js.map +1 -0
  144. package/dist/recipes/outputRegistry.d.ts +28 -0
  145. package/dist/recipes/outputRegistry.js +52 -0
  146. package/dist/recipes/outputRegistry.js.map +1 -0
  147. package/dist/recipes/scheduler.d.ts +23 -7
  148. package/dist/recipes/scheduler.js +135 -41
  149. package/dist/recipes/scheduler.js.map +1 -1
  150. package/dist/recipes/schemaGenerator.d.ts +28 -0
  151. package/dist/recipes/schemaGenerator.js +484 -0
  152. package/dist/recipes/schemaGenerator.js.map +1 -0
  153. package/dist/recipes/templateEngine.d.ts +62 -0
  154. package/dist/recipes/templateEngine.js +182 -0
  155. package/dist/recipes/templateEngine.js.map +1 -0
  156. package/dist/recipes/toolRegistry.d.ts +181 -0
  157. package/dist/recipes/toolRegistry.js +300 -0
  158. package/dist/recipes/toolRegistry.js.map +1 -0
  159. package/dist/recipes/tools/calendar.d.ts +6 -0
  160. package/dist/recipes/tools/calendar.js +61 -0
  161. package/dist/recipes/tools/calendar.js.map +1 -0
  162. package/dist/recipes/tools/confluence.d.ts +6 -0
  163. package/dist/recipes/tools/confluence.js +254 -0
  164. package/dist/recipes/tools/confluence.js.map +1 -0
  165. package/dist/recipes/tools/diagnostics.d.ts +6 -0
  166. package/dist/recipes/tools/diagnostics.js +36 -0
  167. package/dist/recipes/tools/diagnostics.js.map +1 -0
  168. package/dist/recipes/tools/file.d.ts +6 -0
  169. package/dist/recipes/tools/file.js +170 -0
  170. package/dist/recipes/tools/file.js.map +1 -0
  171. package/dist/recipes/tools/git.d.ts +6 -0
  172. package/dist/recipes/tools/git.js +63 -0
  173. package/dist/recipes/tools/git.js.map +1 -0
  174. package/dist/recipes/tools/github.d.ts +6 -0
  175. package/dist/recipes/tools/github.js +91 -0
  176. package/dist/recipes/tools/github.js.map +1 -0
  177. package/dist/recipes/tools/gmail.d.ts +6 -0
  178. package/dist/recipes/tools/gmail.js +210 -0
  179. package/dist/recipes/tools/gmail.js.map +1 -0
  180. package/dist/recipes/tools/index.d.ts +18 -0
  181. package/dist/recipes/tools/index.js +21 -0
  182. package/dist/recipes/tools/index.js.map +1 -0
  183. package/dist/recipes/tools/linear.d.ts +6 -0
  184. package/dist/recipes/tools/linear.js +83 -0
  185. package/dist/recipes/tools/linear.js.map +1 -0
  186. package/dist/recipes/tools/notion.d.ts +6 -0
  187. package/dist/recipes/tools/notion.js +278 -0
  188. package/dist/recipes/tools/notion.js.map +1 -0
  189. package/dist/recipes/tools/slack.d.ts +6 -0
  190. package/dist/recipes/tools/slack.js +72 -0
  191. package/dist/recipes/tools/slack.js.map +1 -0
  192. package/dist/recipes/tools/zendesk.d.ts +6 -0
  193. package/dist/recipes/tools/zendesk.js +245 -0
  194. package/dist/recipes/tools/zendesk.js.map +1 -0
  195. package/dist/recipes/yamlRunner.d.ts +79 -0
  196. package/dist/recipes/yamlRunner.js +612 -346
  197. package/dist/recipes/yamlRunner.js.map +1 -1
  198. package/dist/recipesHttp.d.ts +14 -1
  199. package/dist/recipesHttp.js +21 -4
  200. package/dist/recipesHttp.js.map +1 -1
  201. package/dist/riskTier.js +1 -0
  202. package/dist/riskTier.js.map +1 -1
  203. package/dist/runLog.d.ts +23 -0
  204. package/dist/runLog.js +56 -1
  205. package/dist/runLog.js.map +1 -1
  206. package/dist/server.d.ts +19 -1
  207. package/dist/server.js +682 -31
  208. package/dist/server.js.map +1 -1
  209. package/dist/streamableHttp.js +2 -0
  210. package/dist/streamableHttp.js.map +1 -1
  211. package/dist/tools/addLinearComment.d.ts +55 -0
  212. package/dist/tools/addLinearComment.js +72 -0
  213. package/dist/tools/addLinearComment.js.map +1 -0
  214. package/dist/tools/bridgeDoctor.js +2 -2
  215. package/dist/tools/bridgeDoctor.js.map +1 -1
  216. package/dist/tools/createLinearIssue.d.ts +84 -0
  217. package/dist/tools/createLinearIssue.js +146 -0
  218. package/dist/tools/createLinearIssue.js.map +1 -0
  219. package/dist/tools/ctxGetTaskContext.d.ts +4 -1
  220. package/dist/tools/ctxGetTaskContext.js +45 -2
  221. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  222. package/dist/tools/fetchCalendarEvents.d.ts +94 -0
  223. package/dist/tools/fetchCalendarEvents.js +97 -0
  224. package/dist/tools/fetchCalendarEvents.js.map +1 -0
  225. package/dist/tools/fetchGithubIssue.d.ts +80 -0
  226. package/dist/tools/fetchGithubIssue.js +84 -0
  227. package/dist/tools/fetchGithubIssue.js.map +1 -0
  228. package/dist/tools/fetchGithubPR.d.ts +89 -0
  229. package/dist/tools/fetchGithubPR.js +96 -0
  230. package/dist/tools/fetchGithubPR.js.map +1 -0
  231. package/dist/tools/fetchLinearIssue.d.ts +112 -0
  232. package/dist/tools/fetchLinearIssue.js +129 -0
  233. package/dist/tools/fetchLinearIssue.js.map +1 -0
  234. package/dist/tools/fetchSentryIssue.d.ts +143 -0
  235. package/dist/tools/fetchSentryIssue.js +150 -0
  236. package/dist/tools/fetchSentryIssue.js.map +1 -0
  237. package/dist/tools/fetchSlackProfile.d.ts +43 -0
  238. package/dist/tools/fetchSlackProfile.js +46 -0
  239. package/dist/tools/fetchSlackProfile.js.map +1 -0
  240. package/dist/tools/getConnectorStatus.d.ts +58 -0
  241. package/dist/tools/getConnectorStatus.js +56 -0
  242. package/dist/tools/getConnectorStatus.js.map +1 -0
  243. package/dist/tools/github/actions.js +4 -2
  244. package/dist/tools/github/actions.js.map +1 -1
  245. package/dist/tools/github/composite.d.ts +339 -0
  246. package/dist/tools/github/composite.js +343 -0
  247. package/dist/tools/github/composite.js.map +1 -0
  248. package/dist/tools/github/index.d.ts +2 -1
  249. package/dist/tools/github/index.js +2 -1
  250. package/dist/tools/github/index.js.map +1 -1
  251. package/dist/tools/github/issues.js +8 -4
  252. package/dist/tools/github/issues.js.map +1 -1
  253. package/dist/tools/github/pr.d.ts +122 -0
  254. package/dist/tools/github/pr.js +195 -5
  255. package/dist/tools/github/pr.js.map +1 -1
  256. package/dist/tools/index.js +36 -1
  257. package/dist/tools/index.js.map +1 -1
  258. package/dist/tools/searchTools.js +1 -1
  259. package/dist/tools/searchTools.js.map +1 -1
  260. package/dist/tools/slackListChannels.d.ts +65 -0
  261. package/dist/tools/slackListChannels.js +70 -0
  262. package/dist/tools/slackListChannels.js.map +1 -0
  263. package/dist/tools/slackPostMessage.d.ts +57 -0
  264. package/dist/tools/slackPostMessage.js +77 -0
  265. package/dist/tools/slackPostMessage.js.map +1 -0
  266. package/dist/tools/updateLinearIssue.d.ts +89 -0
  267. package/dist/tools/updateLinearIssue.js +117 -0
  268. package/dist/tools/updateLinearIssue.js.map +1 -0
  269. package/dist/transport.d.ts +7 -1
  270. package/dist/transport.js +85 -11
  271. package/dist/transport.js.map +1 -1
  272. package/package.json +4 -2
  273. package/scripts/start-all.sh +56 -19
  274. package/templates/automation-policies/recipe-authoring.json +25 -0
  275. package/templates/automation-policy.example.json +6 -0
  276. package/templates/co.patchwork-os.bridge.plist +34 -0
  277. package/templates/recipes/ctx-loop-test.yaml +75 -0
  278. package/templates/recipes/lint-on-save.yaml +1 -2
  279. package/templates/recipes/morning-brief-slack.yaml +57 -0
  280. package/templates/recipes/morning-brief.yaml +21 -5
  281. package/templates/recipes/sentry-to-linear.yaml +77 -0
@@ -0,0 +1,424 @@
1
+ /**
2
+ * Notion connector — read/write Notion databases and pages via the Notion API.
3
+ *
4
+ * Auth: API token (internal integration) or OAuth 2.0 (public integration).
5
+ * - Env var: NOTION_TOKEN overrides stored token for CI/headless use.
6
+ * - Stored: getSecretJsonSync("notion") → NotionTokens
7
+ *
8
+ * Tools: queryDatabase, getPage, search, createPage, appendBlock
9
+ *
10
+ * Extends BaseConnector for unified auth, retry, rate-limit, error handling.
11
+ */
12
+ import { unlinkSync } from "node:fs";
13
+ import { homedir } from "node:os";
14
+ import path from "node:path";
15
+ import { BaseConnector, } from "./baseConnector.js";
16
+ import { getSecretJsonSync, storeSecretJsonSync } from "./tokenStorage.js";
17
+ const NOTION_API = "https://api.notion.com/v1";
18
+ const NOTION_VERSION = "2022-06-28";
19
+ // ------------------------------------------------------------------ token helpers
20
+ export function loadTokens() {
21
+ const envToken = process.env.NOTION_TOKEN;
22
+ if (envToken) {
23
+ return {
24
+ accessToken: envToken,
25
+ connected_at: new Date().toISOString(),
26
+ };
27
+ }
28
+ return getSecretJsonSync("notion");
29
+ }
30
+ export function saveTokens(tokens) {
31
+ storeSecretJsonSync("notion", tokens);
32
+ }
33
+ export function clearTokens() {
34
+ try {
35
+ const p = path.join(homedir(), ".patchwork", "tokens", "notion.json");
36
+ unlinkSync(p);
37
+ }
38
+ catch {
39
+ /* already gone */
40
+ }
41
+ }
42
+ // ------------------------------------------------------------------ connector
43
+ export class NotionConnector extends BaseConnector {
44
+ providerName = "notion";
45
+ // Cached after authenticate(); re-read in getStatus() to stay fresh
46
+ cachedTokens = null;
47
+ getOAuthConfig() {
48
+ return null;
49
+ }
50
+ async authenticate() {
51
+ const tokens = loadTokens();
52
+ if (!tokens) {
53
+ throw new Error("Notion not connected. Run: patchwork connect notion or set NOTION_TOKEN");
54
+ }
55
+ this.cachedTokens = tokens;
56
+ return { token: tokens.accessToken };
57
+ }
58
+ async healthCheck() {
59
+ try {
60
+ const result = await this.apiCall(async (token) => {
61
+ const res = await fetch(`${NOTION_API}/users/me`, {
62
+ headers: this.buildHeaders(token),
63
+ });
64
+ if (!res.ok)
65
+ throw new Error(`HTTP ${res.status}`);
66
+ return res.json();
67
+ });
68
+ if ("error" in result)
69
+ return { ok: false, error: result.error };
70
+ return { ok: true };
71
+ }
72
+ catch (err) {
73
+ return { ok: false, error: this.normalizeError(err) };
74
+ }
75
+ }
76
+ normalizeError(error) {
77
+ if (error instanceof Response ||
78
+ (error && typeof error === "object" && "status" in error)) {
79
+ const status = error.status;
80
+ if (status === 401)
81
+ return {
82
+ code: "auth_expired",
83
+ message: "Notion token expired or invalid",
84
+ retryable: false,
85
+ suggestedAction: "Reconnect: patchwork connect notion",
86
+ };
87
+ if (status === 403)
88
+ return {
89
+ code: "permission_denied",
90
+ message: "Notion integration lacks permission for this resource",
91
+ retryable: false,
92
+ suggestedAction: "Share the page/database with your integration",
93
+ };
94
+ if (status === 404)
95
+ return {
96
+ code: "not_found",
97
+ message: "Notion page or database not found",
98
+ retryable: false,
99
+ };
100
+ if (status === 429)
101
+ return {
102
+ code: "rate_limited",
103
+ message: "Notion API rate limit exceeded",
104
+ retryable: true,
105
+ suggestedAction: "Wait and retry",
106
+ };
107
+ return {
108
+ code: "provider_error",
109
+ message: `Notion API error: HTTP ${status}`,
110
+ retryable: status >= 500,
111
+ };
112
+ }
113
+ if (error instanceof Error) {
114
+ if (error.message.includes("ENOTFOUND") ||
115
+ error.message.includes("ECONNREFUSED")) {
116
+ return {
117
+ code: "network_error",
118
+ message: `Cannot reach Notion API: ${error.message}`,
119
+ retryable: true,
120
+ };
121
+ }
122
+ }
123
+ return {
124
+ code: "provider_error",
125
+ message: error instanceof Error ? error.message : String(error),
126
+ retryable: false,
127
+ };
128
+ }
129
+ getStatus() {
130
+ const tokens = loadTokens();
131
+ return {
132
+ id: "notion",
133
+ status: tokens ? "connected" : "disconnected",
134
+ lastSync: tokens?.connected_at,
135
+ workspace: tokens?.workspaceName,
136
+ };
137
+ }
138
+ buildHeaders(token) {
139
+ return {
140
+ Authorization: `Bearer ${token}`,
141
+ "Content-Type": "application/json",
142
+ "Notion-Version": NOTION_VERSION,
143
+ };
144
+ }
145
+ // ---------------------------------------------------------------- read ops
146
+ async queryDatabase(databaseId, filter, sorts, pageSize = 20) {
147
+ const result = await this.apiCall(async (token) => {
148
+ const body = {
149
+ page_size: Math.min(pageSize, 100),
150
+ };
151
+ if (filter)
152
+ body.filter = filter;
153
+ if (sorts)
154
+ body.sorts = sorts;
155
+ const res = await fetch(`${NOTION_API}/databases/${normalizeId(databaseId)}/query`, {
156
+ method: "POST",
157
+ headers: this.buildHeaders(token),
158
+ body: JSON.stringify(body),
159
+ });
160
+ if (!res.ok) {
161
+ const err = (await res.json().catch(() => ({})));
162
+ throw Object.assign(new Error(err.message ?? `HTTP ${res.status}`), {
163
+ status: res.status,
164
+ });
165
+ }
166
+ return res.json();
167
+ });
168
+ if ("error" in result)
169
+ throw new Error(result.error.message);
170
+ return result.data;
171
+ }
172
+ async getPage(pageId) {
173
+ const result = await this.apiCall(async (token) => {
174
+ const res = await fetch(`${NOTION_API}/pages/${normalizeId(pageId)}`, {
175
+ headers: this.buildHeaders(token),
176
+ });
177
+ if (!res.ok) {
178
+ const err = (await res.json().catch(() => ({})));
179
+ throw Object.assign(new Error(err.message ?? `HTTP ${res.status}`), {
180
+ status: res.status,
181
+ });
182
+ }
183
+ return res.json();
184
+ });
185
+ if ("error" in result)
186
+ throw new Error(result.error.message);
187
+ return result.data;
188
+ }
189
+ async search(query, filterType, pageSize = 10) {
190
+ const result = await this.apiCall(async (token) => {
191
+ const body = {
192
+ query,
193
+ page_size: Math.min(pageSize, 100),
194
+ };
195
+ if (filterType)
196
+ body.filter = { value: filterType, property: "object" };
197
+ const res = await fetch(`${NOTION_API}/search`, {
198
+ method: "POST",
199
+ headers: this.buildHeaders(token),
200
+ body: JSON.stringify(body),
201
+ });
202
+ if (!res.ok) {
203
+ const err = (await res.json().catch(() => ({})));
204
+ throw Object.assign(new Error(err.message ?? `HTTP ${res.status}`), {
205
+ status: res.status,
206
+ });
207
+ }
208
+ return res.json();
209
+ });
210
+ if ("error" in result)
211
+ throw new Error(result.error.message);
212
+ return result.data;
213
+ }
214
+ // ---------------------------------------------------------------- write ops
215
+ async createPage(params) {
216
+ const result = await this.apiCall(async (token) => {
217
+ const parent = params.parentType === "database"
218
+ ? { database_id: normalizeId(params.parentId) }
219
+ : { page_id: normalizeId(params.parentId) };
220
+ const properties = params.properties ?? {};
221
+ // Set title property — key is "title" for pages, "Name" for database rows
222
+ const titleKey = params.parentType === "database" ? "Name" : "title";
223
+ properties[titleKey] = {
224
+ title: [{ text: { content: params.title } }],
225
+ };
226
+ const body = { parent, properties };
227
+ if (params.content) {
228
+ body.children = [
229
+ {
230
+ object: "block",
231
+ type: "paragraph",
232
+ paragraph: {
233
+ rich_text: [{ type: "text", text: { content: params.content } }],
234
+ },
235
+ },
236
+ ];
237
+ }
238
+ const res = await fetch(`${NOTION_API}/pages`, {
239
+ method: "POST",
240
+ headers: this.buildHeaders(token),
241
+ body: JSON.stringify(body),
242
+ });
243
+ if (!res.ok) {
244
+ const err = (await res.json().catch(() => ({})));
245
+ throw Object.assign(new Error(err.message ?? `HTTP ${res.status}`), {
246
+ status: res.status,
247
+ });
248
+ }
249
+ return res.json();
250
+ });
251
+ if ("error" in result)
252
+ throw new Error(result.error.message);
253
+ return result.data;
254
+ }
255
+ async appendBlock(params) {
256
+ const result = await this.apiCall(async (token) => {
257
+ const type = params.blockType ?? "paragraph";
258
+ const richText = [{ type: "text", text: { content: params.content } }];
259
+ const block = {
260
+ object: "block",
261
+ type,
262
+ [type]: { rich_text: richText },
263
+ };
264
+ const res = await fetch(`${NOTION_API}/blocks/${normalizeId(params.pageId)}/children`, {
265
+ method: "PATCH",
266
+ headers: this.buildHeaders(token),
267
+ body: JSON.stringify({ children: [block] }),
268
+ });
269
+ if (!res.ok) {
270
+ const err = (await res.json().catch(() => ({})));
271
+ throw Object.assign(new Error(err.message ?? `HTTP ${res.status}`), {
272
+ status: res.status,
273
+ });
274
+ }
275
+ return res.json();
276
+ });
277
+ if ("error" in result)
278
+ throw new Error(result.error.message);
279
+ return result.data;
280
+ }
281
+ }
282
+ // ------------------------------------------------------------------ helpers
283
+ /** Normalize Notion IDs — strip hyphens if present, add back in UUID format. */
284
+ function normalizeId(id) {
285
+ const stripped = id.replace(/-/g, "");
286
+ if (stripped.length === 32) {
287
+ return `${stripped.slice(0, 8)}-${stripped.slice(8, 12)}-${stripped.slice(12, 16)}-${stripped.slice(16, 20)}-${stripped.slice(20)}`;
288
+ }
289
+ return id;
290
+ }
291
+ // ------------------------------------------------------------------ singleton
292
+ let _instance = null;
293
+ export function getNotionConnector() {
294
+ if (!_instance)
295
+ _instance = new NotionConnector();
296
+ return _instance;
297
+ }
298
+ export function resetNotionConnector() {
299
+ _instance = null;
300
+ }
301
+ // ------------------------------------------------------------------ convenience re-exports
302
+ export { loadTokens as isConnected };
303
+ /**
304
+ * POST /connections/notion/connect { token: "secret_..." }
305
+ * Stores the integration token and verifies it by calling /users/me.
306
+ */
307
+ export async function handleNotionConnect(body) {
308
+ let token;
309
+ try {
310
+ const parsed = JSON.parse(body);
311
+ if (typeof parsed.token !== "string" ||
312
+ !parsed.token.startsWith("secret_")) {
313
+ return {
314
+ status: 400,
315
+ contentType: "application/json",
316
+ body: JSON.stringify({
317
+ ok: false,
318
+ error: 'Notion integration token must start with "secret_". Find it at https://www.notion.so/my-integrations',
319
+ }),
320
+ };
321
+ }
322
+ token = parsed.token;
323
+ }
324
+ catch {
325
+ return {
326
+ status: 400,
327
+ contentType: "application/json",
328
+ body: JSON.stringify({ ok: false, error: "Invalid JSON body" }),
329
+ };
330
+ }
331
+ try {
332
+ const res = await fetch(`${NOTION_API}/users/me`, {
333
+ headers: {
334
+ Authorization: `Bearer ${token}`,
335
+ "Notion-Version": NOTION_VERSION,
336
+ },
337
+ });
338
+ if (!res.ok) {
339
+ return {
340
+ status: 401,
341
+ contentType: "application/json",
342
+ body: JSON.stringify({
343
+ ok: false,
344
+ error: "Token rejected by Notion API — check the token is valid and the integration is active",
345
+ }),
346
+ };
347
+ }
348
+ const user = (await res.json());
349
+ const tokens = {
350
+ accessToken: token,
351
+ workspaceName: user.bot?.workspace_name,
352
+ workspaceId: user.bot?.owner?.workspace_id,
353
+ connected_at: new Date().toISOString(),
354
+ };
355
+ saveTokens(tokens);
356
+ resetNotionConnector();
357
+ return {
358
+ status: 200,
359
+ contentType: "application/json",
360
+ body: JSON.stringify({
361
+ ok: true,
362
+ workspace: tokens.workspaceName ?? "unknown",
363
+ connectedAt: tokens.connected_at,
364
+ }),
365
+ };
366
+ }
367
+ catch (err) {
368
+ return {
369
+ status: 500,
370
+ contentType: "application/json",
371
+ body: JSON.stringify({
372
+ ok: false,
373
+ error: err instanceof Error ? err.message : String(err),
374
+ }),
375
+ };
376
+ }
377
+ }
378
+ /**
379
+ * POST /connections/notion/test
380
+ * Verifies stored token is still valid.
381
+ */
382
+ export async function handleNotionTest() {
383
+ const tokens = loadTokens();
384
+ if (!tokens) {
385
+ return {
386
+ status: 400,
387
+ contentType: "application/json",
388
+ body: JSON.stringify({ ok: false, error: "Notion not connected" }),
389
+ };
390
+ }
391
+ try {
392
+ const connector = getNotionConnector();
393
+ const check = await connector.healthCheck();
394
+ return {
395
+ status: check.ok ? 200 : 401,
396
+ contentType: "application/json",
397
+ body: JSON.stringify(check.ok ? { ok: true } : { ok: false, error: check.error?.message }),
398
+ };
399
+ }
400
+ catch (err) {
401
+ return {
402
+ status: 500,
403
+ contentType: "application/json",
404
+ body: JSON.stringify({
405
+ ok: false,
406
+ error: err instanceof Error ? err.message : String(err),
407
+ }),
408
+ };
409
+ }
410
+ }
411
+ /**
412
+ * DELETE /connections/notion
413
+ * Removes stored token.
414
+ */
415
+ export function handleNotionDisconnect() {
416
+ clearTokens();
417
+ resetNotionConnector();
418
+ return {
419
+ status: 200,
420
+ contentType: "application/json",
421
+ body: JSON.stringify({ ok: true }),
422
+ };
423
+ }
424
+ //# sourceMappingURL=notion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notion.js","sourceRoot":"","sources":["../../src/connectors/notion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Sentry connector — routes through Sentry's official MCP server.
3
+ *
4
+ * Endpoint: https://mcp.sentry.dev/mcp
5
+ * Auth: OAuth 2.1 w/ PKCE; dynamic client registration (RFC 7591).
6
+ *
7
+ * HTTP routes (wired in src/server.ts):
8
+ * GET /connections/sentry/authorize — returns { url } for popup
9
+ * GET /connections/sentry/callback — token exchange
10
+ * POST /connections/sentry/test — ping MCP server
11
+ * DELETE /connections/sentry — revoke + delete token
12
+ *
13
+ * MCP tool: fetchSentryIssue — fetches a Sentry issue/event and returns
14
+ * the stack trace string, ready to pass into enrichStackTrace.
15
+ */
16
+ export interface SentryTokens {
17
+ auth_token: string;
18
+ org?: string;
19
+ connected_at: string;
20
+ }
21
+ export interface ConnectorStatus {
22
+ id: string;
23
+ status: "connected" | "disconnected";
24
+ lastSync?: string;
25
+ org?: string;
26
+ }
27
+ export interface ConnectorHandlerResult {
28
+ status: number;
29
+ body: string;
30
+ contentType?: string;
31
+ redirect?: string;
32
+ }
33
+ export declare function loadTokens(): SentryTokens | null;
34
+ export declare function getStatus(): ConnectorStatus;
35
+ export declare function fetchIssueStackTrace(issueIdOrUrl: string, signal?: AbortSignal): Promise<{
36
+ stackTrace: string;
37
+ title: string;
38
+ issueId: string;
39
+ }>;
40
+ export declare function handleSentryAuthorize(): Promise<ConnectorHandlerResult>;
41
+ export declare function handleSentryCallback(code: string | null, state: string | null, error: string | null): Promise<ConnectorHandlerResult>;
42
+ export declare function handleSentryTest(): Promise<ConnectorHandlerResult>;
43
+ export declare function handleSentryDisconnect(): Promise<ConnectorHandlerResult>;
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Sentry connector — routes through Sentry's official MCP server.
3
+ *
4
+ * Endpoint: https://mcp.sentry.dev/mcp
5
+ * Auth: OAuth 2.1 w/ PKCE; dynamic client registration (RFC 7591).
6
+ *
7
+ * HTTP routes (wired in src/server.ts):
8
+ * GET /connections/sentry/authorize — returns { url } for popup
9
+ * GET /connections/sentry/callback — token exchange
10
+ * POST /connections/sentry/test — ping MCP server
11
+ * DELETE /connections/sentry — revoke + delete token
12
+ *
13
+ * MCP tool: fetchSentryIssue — fetches a Sentry issue/event and returns
14
+ * the stack trace string, ready to pass into enrichStackTrace.
15
+ */
16
+ import { McpClient } from "./mcpClient.js";
17
+ import { completeAuthorize, getAccessToken, loadTokenFile, revoke, startAuthorize, updateTokenProfile, vendorConfig, } from "./mcpOAuth.js";
18
+ const SENTRY_MCP_ENDPOINT = "https://mcp.sentry.dev/mcp";
19
+ // ── MCP client ───────────────────────────────────────────────────────────────
20
+ let _client = null;
21
+ function client() {
22
+ if (!_client) {
23
+ _client = new McpClient(SENTRY_MCP_ENDPOINT, () => getAccessToken("sentry"));
24
+ }
25
+ return _client;
26
+ }
27
+ // ── Back-compat ──────────────────────────────────────────────────────────────
28
+ export function loadTokens() {
29
+ const file = loadTokenFile("sentry");
30
+ if (!file)
31
+ return null;
32
+ return {
33
+ auth_token: file.access_token,
34
+ org: file.profile?.org,
35
+ connected_at: file.connected_at,
36
+ };
37
+ }
38
+ export function getStatus() {
39
+ const file = loadTokenFile("sentry");
40
+ return {
41
+ id: "sentry",
42
+ status: file ? "connected" : "disconnected",
43
+ lastSync: file?.connected_at,
44
+ org: file?.profile?.org,
45
+ };
46
+ }
47
+ // ── Issue fetch ──────────────────────────────────────────────────────────────
48
+ function extractIssueId(issueIdOrUrl) {
49
+ const urlMatch = issueIdOrUrl.match(/\/issues\/(\d+)/);
50
+ if (urlMatch)
51
+ return urlMatch[1];
52
+ const trimmed = issueIdOrUrl.trim();
53
+ if (/^\d+$/.test(trimmed))
54
+ return trimmed;
55
+ throw new Error(`Cannot parse Sentry issue ID from: ${issueIdOrUrl}`);
56
+ }
57
+ function extractOrgSlug(issueIdOrUrl) {
58
+ const m = issueIdOrUrl.match(/https?:\/\/([^.]+)\.sentry\.io/);
59
+ return m ? m[1] : null;
60
+ }
61
+ function buildSentryIssueUrl(issueId, orgSlug) {
62
+ return `https://${orgSlug}.sentry.io/issues/${issueId}/`;
63
+ }
64
+ export async function fetchIssueStackTrace(issueIdOrUrl, signal) {
65
+ if (!loadTokens())
66
+ throw new Error("Sentry not connected. GET /connections/sentry/authorize first.");
67
+ const issueId = extractIssueId(issueIdOrUrl);
68
+ const orgSlug = extractOrgSlug(issueIdOrUrl) ?? loadTokenFile("sentry")?.profile?.org;
69
+ if (!orgSlug)
70
+ throw new Error("Cannot determine Sentry org slug. Pass full sentry.io issue URL.");
71
+ const issueUrl = buildSentryIssueUrl(issueId, orgSlug);
72
+ const res = await client().callTool("get_sentry_resource", { url: issueUrl }, { signal });
73
+ // get_sentry_resource returns markdown text — extract title and stacktrace
74
+ const text = res.content?.[0]?.text ?? "";
75
+ const titleMatch = text.match(/\*\*Description\*\*:\s*(.+)/);
76
+ const title = titleMatch
77
+ ? titleMatch[1].trim()
78
+ : `Sentry issue ${issueId}`;
79
+ // Extract stacktrace block
80
+ const stMatch = text.match(/```(?:\w+)?\n([\s\S]*?)\n```/);
81
+ if (!stMatch) {
82
+ throw new Error(`Sentry returned no stack trace for issue ${issueId}`);
83
+ }
84
+ const stackTrace = stMatch[1].trim();
85
+ return { stackTrace, title, issueId };
86
+ }
87
+ // ── HTTP handlers ────────────────────────────────────────────────────────────
88
+ export async function handleSentryAuthorize() {
89
+ try {
90
+ const { url } = await startAuthorize(vendorConfig("sentry"));
91
+ return { status: 302, body: "", redirect: url };
92
+ }
93
+ catch (err) {
94
+ return {
95
+ status: 400,
96
+ contentType: "application/json",
97
+ body: JSON.stringify({
98
+ ok: false,
99
+ error: err instanceof Error ? err.message : String(err),
100
+ }),
101
+ };
102
+ }
103
+ }
104
+ export async function handleSentryCallback(code, state, error) {
105
+ if (error) {
106
+ return {
107
+ status: 400,
108
+ contentType: "text/html",
109
+ body: `<html><body><h2>Sentry connect failed</h2><pre>${error}</pre></body></html>`,
110
+ };
111
+ }
112
+ if (!code || !state) {
113
+ return {
114
+ status: 400,
115
+ contentType: "text/html",
116
+ body: `<html><body><h2>Sentry connect failed</h2><pre>missing code/state</pre></body></html>`,
117
+ };
118
+ }
119
+ try {
120
+ await completeAuthorize(vendorConfig("sentry"), code, state);
121
+ // Best-effort org capture
122
+ try {
123
+ const res = await client().callTool("find_organizations", {}, {
124
+ timeoutMs: 10_000,
125
+ });
126
+ const orgs = McpClient.extractJson(res);
127
+ const first = Array.isArray(orgs)
128
+ ? orgs[0]
129
+ : (orgs.organizations ?? [])[0];
130
+ const org = first?.slug;
131
+ if (org) {
132
+ updateTokenProfile("sentry", { org });
133
+ }
134
+ }
135
+ catch {
136
+ // Profile fetch is best-effort
137
+ }
138
+ return {
139
+ status: 200,
140
+ contentType: "text/html",
141
+ body: `<html><body><h2>Sentry connected</h2><script>window.close();</script></body></html>`,
142
+ };
143
+ }
144
+ catch (err) {
145
+ return {
146
+ status: 400,
147
+ contentType: "text/html",
148
+ body: `<html><body><h2>Sentry connect failed</h2><pre>${err instanceof Error ? err.message : String(err)}</pre></body></html>`,
149
+ };
150
+ }
151
+ }
152
+ export async function handleSentryTest() {
153
+ if (!loadTokens()) {
154
+ return {
155
+ status: 400,
156
+ contentType: "application/json",
157
+ body: JSON.stringify({ ok: false, error: "Sentry not connected" }),
158
+ };
159
+ }
160
+ try {
161
+ const ok = await client().ping({ timeoutMs: 10_000 });
162
+ return {
163
+ status: ok ? 200 : 400,
164
+ contentType: "application/json",
165
+ body: JSON.stringify({ ok, message: ok ? "connected" : "ping failed" }),
166
+ };
167
+ }
168
+ catch (err) {
169
+ return {
170
+ status: 400,
171
+ contentType: "application/json",
172
+ body: JSON.stringify({
173
+ ok: false,
174
+ error: err instanceof Error ? err.message : String(err),
175
+ }),
176
+ };
177
+ }
178
+ }
179
+ export async function handleSentryDisconnect() {
180
+ await revoke("sentry");
181
+ _client = null;
182
+ return {
183
+ status: 200,
184
+ contentType: "application/json",
185
+ body: JSON.stringify({ ok: true }),
186
+ };
187
+ }
188
+ //# sourceMappingURL=sentry.js.map