patchwork-os 0.2.0-beta.1 → 0.2.0-beta.10.canary.95

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 (652) hide show
  1. package/README.bridge.md +14 -14
  2. package/README.md +201 -34
  3. package/deploy/README.md +1 -1
  4. package/deploy/bootstrap-vps.sh +14 -9
  5. package/deploy/deploy-dashboard.sh +25 -1
  6. package/deploy/macos/README.md +153 -0
  7. package/deploy/macos/com.patchwork.bridge.plist.template +54 -0
  8. package/deploy/macos/com.patchwork.tunnel.plist.template +76 -0
  9. package/deploy/macos/install-mac-bridge.sh +244 -0
  10. package/deploy/macos/uninstall-mac-bridge.sh +22 -0
  11. package/dist/activityLog.d.ts +6 -0
  12. package/dist/activityLog.js +63 -26
  13. package/dist/activityLog.js.map +1 -1
  14. package/dist/adapters/claude.js +22 -16
  15. package/dist/adapters/claude.js.map +1 -1
  16. package/dist/adapters/gemini.js +15 -11
  17. package/dist/adapters/gemini.js.map +1 -1
  18. package/dist/adapters/openai.js +9 -9
  19. package/dist/adapters/openai.js.map +1 -1
  20. package/dist/ajv2020.d.ts +25 -0
  21. package/dist/ajv2020.js +33 -0
  22. package/dist/ajv2020.js.map +1 -0
  23. package/dist/analyticsAggregator.js +1 -1
  24. package/dist/analyticsAggregator.js.map +1 -1
  25. package/dist/analyticsConfig.d.ts +29 -0
  26. package/dist/analyticsConfig.js +102 -0
  27. package/dist/analyticsConfig.js.map +1 -0
  28. package/dist/analyticsPrefs.d.ts +38 -2
  29. package/dist/analyticsPrefs.js +148 -23
  30. package/dist/analyticsPrefs.js.map +1 -1
  31. package/dist/analyticsSend.d.ts +17 -1
  32. package/dist/analyticsSend.js +67 -5
  33. package/dist/analyticsSend.js.map +1 -1
  34. package/dist/approvalHttp.d.ts +14 -0
  35. package/dist/approvalHttp.js +212 -9
  36. package/dist/approvalHttp.js.map +1 -1
  37. package/dist/approvalInsights.js +13 -5
  38. package/dist/approvalInsights.js.map +1 -1
  39. package/dist/approvalQueue.d.ts +97 -3
  40. package/dist/approvalQueue.js +193 -19
  41. package/dist/approvalQueue.js.map +1 -1
  42. package/dist/approvalSignals.js +19 -11
  43. package/dist/approvalSignals.js.map +1 -1
  44. package/dist/automation.d.ts +35 -10
  45. package/dist/automation.js +133 -37
  46. package/dist/automation.js.map +1 -1
  47. package/dist/automationSuggestions.js +10 -8
  48. package/dist/automationSuggestions.js.map +1 -1
  49. package/dist/bridge.d.ts +2 -0
  50. package/dist/bridge.js +202 -25
  51. package/dist/bridge.js.map +1 -1
  52. package/dist/bridgeLockDiscovery.d.ts +27 -1
  53. package/dist/bridgeLockDiscovery.js +38 -11
  54. package/dist/bridgeLockDiscovery.js.map +1 -1
  55. package/dist/bridgeToken.js +3 -2
  56. package/dist/bridgeToken.js.map +1 -1
  57. package/dist/claudeDriver.js +23 -8
  58. package/dist/claudeDriver.js.map +1 -1
  59. package/dist/claudeOrchestrator.d.ts +1 -1
  60. package/dist/claudeOrchestrator.js +87 -44
  61. package/dist/claudeOrchestrator.js.map +1 -1
  62. package/dist/commands/analytics.d.ts +8 -0
  63. package/dist/commands/analytics.js +134 -0
  64. package/dist/commands/analytics.js.map +1 -0
  65. package/dist/commands/auditEnv.d.ts +36 -0
  66. package/dist/commands/auditEnv.js +202 -0
  67. package/dist/commands/auditEnv.js.map +1 -0
  68. package/dist/commands/dashboard.js +8 -1
  69. package/dist/commands/dashboard.js.map +1 -1
  70. package/dist/commands/doctor.d.ts +35 -0
  71. package/dist/commands/doctor.js +51 -0
  72. package/dist/commands/doctor.js.map +1 -0
  73. package/dist/commands/install.js +3 -0
  74. package/dist/commands/install.js.map +1 -1
  75. package/dist/commands/launchd.js +15 -16
  76. package/dist/commands/launchd.js.map +1 -1
  77. package/dist/commands/patchworkInit.d.ts +5 -0
  78. package/dist/commands/patchworkInit.js +89 -7
  79. package/dist/commands/patchworkInit.js.map +1 -1
  80. package/dist/commands/recipe.d.ts +110 -0
  81. package/dist/commands/recipe.js +512 -9
  82. package/dist/commands/recipe.js.map +1 -1
  83. package/dist/commands/recipeInstall.js +11 -4
  84. package/dist/commands/recipeInstall.js.map +1 -1
  85. package/dist/commands/shadowScan.d.ts +34 -0
  86. package/dist/commands/shadowScan.js +142 -0
  87. package/dist/commands/shadowScan.js.map +1 -0
  88. package/dist/commands/task.js +2 -2
  89. package/dist/commands/task.js.map +1 -1
  90. package/dist/commands/tools.d.ts +20 -1
  91. package/dist/commands/tools.js +112 -3
  92. package/dist/commands/tools.js.map +1 -1
  93. package/dist/commands/tracesImport.js +21 -4
  94. package/dist/commands/tracesImport.js.map +1 -1
  95. package/dist/commitIssueLinkLog.d.ts +16 -0
  96. package/dist/commitIssueLinkLog.js +110 -24
  97. package/dist/commitIssueLinkLog.js.map +1 -1
  98. package/dist/companions/registry.js +15 -7
  99. package/dist/companions/registry.js.map +1 -1
  100. package/dist/config.d.ts +47 -3
  101. package/dist/config.js +111 -21
  102. package/dist/config.js.map +1 -1
  103. package/dist/connectorRoutes.js +866 -57
  104. package/dist/connectorRoutes.js.map +1 -1
  105. package/dist/connectors/airtable.d.ts +230 -0
  106. package/dist/connectors/airtable.js +700 -0
  107. package/dist/connectors/airtable.js.map +1 -0
  108. package/dist/connectors/asana.js +13 -9
  109. package/dist/connectors/asana.js.map +1 -1
  110. package/dist/connectors/baseConnector.js +25 -3
  111. package/dist/connectors/baseConnector.js.map +1 -1
  112. package/dist/connectors/caldiy.d.ts +137 -0
  113. package/dist/connectors/caldiy.js +424 -0
  114. package/dist/connectors/caldiy.js.map +1 -0
  115. package/dist/connectors/circleci.d.ts +162 -0
  116. package/dist/connectors/circleci.js +576 -0
  117. package/dist/connectors/circleci.js.map +1 -0
  118. package/dist/connectors/cloudflare.d.ts +132 -0
  119. package/dist/connectors/cloudflare.js +622 -0
  120. package/dist/connectors/cloudflare.js.map +1 -0
  121. package/dist/connectors/confluence.js +35 -0
  122. package/dist/connectors/confluence.js.map +1 -1
  123. package/dist/connectors/connectorRedirectUri.d.ts +30 -0
  124. package/dist/connectors/connectorRedirectUri.js +38 -0
  125. package/dist/connectors/connectorRedirectUri.js.map +1 -0
  126. package/dist/connectors/connectorRegistry.d.ts +63 -0
  127. package/dist/connectors/connectorRegistry.js +354 -0
  128. package/dist/connectors/connectorRegistry.js.map +1 -0
  129. package/dist/connectors/datadog.js +33 -4
  130. package/dist/connectors/datadog.js.map +1 -1
  131. package/dist/connectors/discord.js +14 -10
  132. package/dist/connectors/discord.js.map +1 -1
  133. package/dist/connectors/elasticsearch.d.ts +141 -0
  134. package/dist/connectors/elasticsearch.js +601 -0
  135. package/dist/connectors/elasticsearch.js.map +1 -0
  136. package/dist/connectors/figma.d.ts +130 -0
  137. package/dist/connectors/figma.js +387 -0
  138. package/dist/connectors/figma.js.map +1 -0
  139. package/dist/connectors/github.d.ts +17 -0
  140. package/dist/connectors/github.js +53 -2
  141. package/dist/connectors/github.js.map +1 -1
  142. package/dist/connectors/gitlab.js +12 -6
  143. package/dist/connectors/gitlab.js.map +1 -1
  144. package/dist/connectors/gmail.js +72 -21
  145. package/dist/connectors/gmail.js.map +1 -1
  146. package/dist/connectors/googleCalendar.js +8 -8
  147. package/dist/connectors/googleCalendar.js.map +1 -1
  148. package/dist/connectors/googleDocs.d.ts +103 -0
  149. package/dist/connectors/googleDocs.js +409 -0
  150. package/dist/connectors/googleDocs.js.map +1 -0
  151. package/dist/connectors/googleDrive.d.ts +12 -0
  152. package/dist/connectors/googleDrive.js +35 -8
  153. package/dist/connectors/googleDrive.js.map +1 -1
  154. package/dist/connectors/grafana.d.ts +133 -0
  155. package/dist/connectors/grafana.js +478 -0
  156. package/dist/connectors/grafana.js.map +1 -0
  157. package/dist/connectors/jira.d.ts +25 -0
  158. package/dist/connectors/jira.js +227 -1
  159. package/dist/connectors/jira.js.map +1 -1
  160. package/dist/connectors/linear.js +1 -1
  161. package/dist/connectors/linear.js.map +1 -1
  162. package/dist/connectors/mcpOAuth.js +78 -16
  163. package/dist/connectors/mcpOAuth.js.map +1 -1
  164. package/dist/connectors/monday.d.ts +217 -0
  165. package/dist/connectors/monday.js +655 -0
  166. package/dist/connectors/monday.js.map +1 -0
  167. package/dist/connectors/mongodb.d.ts +139 -0
  168. package/dist/connectors/mongodb.js +455 -0
  169. package/dist/connectors/mongodb.js.map +1 -0
  170. package/dist/connectors/oauthStateStore.d.ts +20 -0
  171. package/dist/connectors/oauthStateStore.js +94 -4
  172. package/dist/connectors/oauthStateStore.js.map +1 -1
  173. package/dist/connectors/obsidian.d.ts +83 -0
  174. package/dist/connectors/obsidian.js +441 -0
  175. package/dist/connectors/obsidian.js.map +1 -0
  176. package/dist/connectors/paystack.d.ts +159 -0
  177. package/dist/connectors/paystack.js +607 -0
  178. package/dist/connectors/paystack.js.map +1 -0
  179. package/dist/connectors/pipedrive.d.ts +189 -0
  180. package/dist/connectors/pipedrive.js +559 -0
  181. package/dist/connectors/pipedrive.js.map +1 -0
  182. package/dist/connectors/postgres.d.ts +127 -0
  183. package/dist/connectors/postgres.js +512 -0
  184. package/dist/connectors/postgres.js.map +1 -0
  185. package/dist/connectors/posthog.d.ts +119 -0
  186. package/dist/connectors/posthog.js +479 -0
  187. package/dist/connectors/posthog.js.map +1 -0
  188. package/dist/connectors/redis.d.ts +140 -0
  189. package/dist/connectors/redis.js +571 -0
  190. package/dist/connectors/redis.js.map +1 -0
  191. package/dist/connectors/resend.d.ts +131 -0
  192. package/dist/connectors/resend.js +529 -0
  193. package/dist/connectors/resend.js.map +1 -0
  194. package/dist/connectors/salesforce.d.ts +136 -0
  195. package/dist/connectors/salesforce.js +603 -0
  196. package/dist/connectors/salesforce.js.map +1 -0
  197. package/dist/connectors/secrets.d.ts +51 -0
  198. package/dist/connectors/secrets.js +102 -0
  199. package/dist/connectors/secrets.js.map +1 -0
  200. package/dist/connectors/sendgrid.d.ts +102 -0
  201. package/dist/connectors/sendgrid.js +423 -0
  202. package/dist/connectors/sendgrid.js.map +1 -0
  203. package/dist/connectors/shopify.d.ts +145 -0
  204. package/dist/connectors/shopify.js +502 -0
  205. package/dist/connectors/shopify.js.map +1 -0
  206. package/dist/connectors/slack.d.ts +1 -1
  207. package/dist/connectors/slack.js +61 -9
  208. package/dist/connectors/slack.js.map +1 -1
  209. package/dist/connectors/snowflake.d.ts +119 -0
  210. package/dist/connectors/snowflake.js +615 -0
  211. package/dist/connectors/snowflake.js.map +1 -0
  212. package/dist/connectors/supabase.d.ts +92 -0
  213. package/dist/connectors/supabase.js +630 -0
  214. package/dist/connectors/supabase.js.map +1 -0
  215. package/dist/connectors/todoist.d.ts +117 -0
  216. package/dist/connectors/todoist.js +485 -0
  217. package/dist/connectors/todoist.js.map +1 -0
  218. package/dist/connectors/tokenStorage.js +56 -14
  219. package/dist/connectors/tokenStorage.js.map +1 -1
  220. package/dist/connectors/twilio.d.ts +118 -0
  221. package/dist/connectors/twilio.js +475 -0
  222. package/dist/connectors/twilio.js.map +1 -0
  223. package/dist/connectors/vercel.d.ts +110 -0
  224. package/dist/connectors/vercel.js +479 -0
  225. package/dist/connectors/vercel.js.map +1 -0
  226. package/dist/connectors/webflow.d.ts +118 -0
  227. package/dist/connectors/webflow.js +393 -0
  228. package/dist/connectors/webflow.js.map +1 -0
  229. package/dist/connectors/woocommerce.d.ts +220 -0
  230. package/dist/connectors/woocommerce.js +464 -0
  231. package/dist/connectors/woocommerce.js.map +1 -0
  232. package/dist/crypto.js +7 -2
  233. package/dist/crypto.js.map +1 -1
  234. package/dist/decisionReplay.js +15 -6
  235. package/dist/decisionReplay.js.map +1 -1
  236. package/dist/decisionTraceLog.d.ts +28 -0
  237. package/dist/decisionTraceLog.js +156 -29
  238. package/dist/decisionTraceLog.js.map +1 -1
  239. package/dist/drivers/claude/api.js +5 -4
  240. package/dist/drivers/claude/api.js.map +1 -1
  241. package/dist/drivers/claude/envSanitizer.d.ts +0 -6
  242. package/dist/drivers/claude/envSanitizer.js +17 -2
  243. package/dist/drivers/claude/envSanitizer.js.map +1 -1
  244. package/dist/drivers/claude/streamParser.js +5 -4
  245. package/dist/drivers/claude/streamParser.js.map +1 -1
  246. package/dist/drivers/claude/subprocess.js +22 -3
  247. package/dist/drivers/claude/subprocess.js.map +1 -1
  248. package/dist/drivers/gemini/index.d.ts +22 -0
  249. package/dist/drivers/gemini/index.js +256 -129
  250. package/dist/drivers/gemini/index.js.map +1 -1
  251. package/dist/drivers/local/index.d.ts +17 -0
  252. package/dist/drivers/local/index.js +99 -0
  253. package/dist/drivers/local/index.js.map +1 -1
  254. package/dist/drivers/openai/index.js +30 -2
  255. package/dist/drivers/openai/index.js.map +1 -1
  256. package/dist/extensionClient.d.ts +37 -4
  257. package/dist/extensionClient.js +58 -16
  258. package/dist/extensionClient.js.map +1 -1
  259. package/dist/featureFlags.d.ts +91 -0
  260. package/dist/featureFlags.js +174 -3
  261. package/dist/featureFlags.js.map +1 -1
  262. package/dist/fileLock.js +21 -12
  263. package/dist/fileLock.js.map +1 -1
  264. package/dist/fileLockSync.d.ts +67 -0
  265. package/dist/fileLockSync.js +126 -0
  266. package/dist/fileLockSync.js.map +1 -0
  267. package/dist/fp/activityAnalytics.js +26 -12
  268. package/dist/fp/activityAnalytics.js.map +1 -1
  269. package/dist/fp/automationInterpreter.d.ts +15 -1
  270. package/dist/fp/automationInterpreter.js +217 -82
  271. package/dist/fp/automationInterpreter.js.map +1 -1
  272. package/dist/fp/automationProgram.d.ts +30 -0
  273. package/dist/fp/automationProgram.js.map +1 -1
  274. package/dist/fp/automationState.d.ts +24 -5
  275. package/dist/fp/automationState.js +56 -9
  276. package/dist/fp/automationState.js.map +1 -1
  277. package/dist/fp/automationUtils.js +1 -1
  278. package/dist/fp/automationUtils.js.map +1 -1
  279. package/dist/fp/commandDescription.js +7 -1
  280. package/dist/fp/commandDescription.js.map +1 -1
  281. package/dist/fp/extensionSnapshot.js +8 -2
  282. package/dist/fp/extensionSnapshot.js.map +1 -1
  283. package/dist/fp/interpreterContext.d.ts +66 -1
  284. package/dist/fp/interpreterContext.js +91 -1
  285. package/dist/fp/interpreterContext.js.map +1 -1
  286. package/dist/fp/policyParser.js +29 -1
  287. package/dist/fp/policyParser.js.map +1 -1
  288. package/dist/fsWatchWithFallback.d.ts +36 -0
  289. package/dist/fsWatchWithFallback.js +128 -0
  290. package/dist/fsWatchWithFallback.js.map +1 -0
  291. package/dist/haltPushDispatch.d.ts +33 -0
  292. package/dist/haltPushDispatch.js +103 -0
  293. package/dist/haltPushDispatch.js.map +1 -0
  294. package/dist/httpBodyValidation.d.ts +41 -0
  295. package/dist/httpBodyValidation.js +45 -0
  296. package/dist/httpBodyValidation.js.map +1 -0
  297. package/dist/inboxRoutes.d.ts +22 -0
  298. package/dist/inboxRoutes.js +61 -1
  299. package/dist/inboxRoutes.js.map +1 -1
  300. package/dist/index.js +1053 -76
  301. package/dist/index.js.map +1 -1
  302. package/dist/installGuard.js +6 -2
  303. package/dist/installGuard.js.map +1 -1
  304. package/dist/lockfile.js +31 -4
  305. package/dist/lockfile.js.map +1 -1
  306. package/dist/oauth.d.ts +9 -0
  307. package/dist/oauth.js +33 -0
  308. package/dist/oauth.js.map +1 -1
  309. package/dist/oauthRoutes.d.ts +1 -1
  310. package/dist/oauthRoutes.js +5 -3
  311. package/dist/oauthRoutes.js.map +1 -1
  312. package/dist/orchestrator/childBridgeRegistry.js +29 -11
  313. package/dist/orchestrator/childBridgeRegistry.js.map +1 -1
  314. package/dist/patchworkConfig.d.ts +44 -1
  315. package/dist/patchworkConfig.js +28 -3
  316. package/dist/patchworkConfig.js.map +1 -1
  317. package/dist/pluginLoader.js +10 -1
  318. package/dist/pluginLoader.js.map +1 -1
  319. package/dist/pluginWatcher.js +12 -14
  320. package/dist/pluginWatcher.js.map +1 -1
  321. package/dist/preToolUseHook.js +3 -2
  322. package/dist/preToolUseHook.js.map +1 -1
  323. package/dist/processTree.d.ts +34 -0
  324. package/dist/processTree.js +105 -0
  325. package/dist/processTree.js.map +1 -0
  326. package/dist/prompts.js +3 -3
  327. package/dist/prompts.js.map +1 -1
  328. package/dist/recipeOrchestration.d.ts +9 -0
  329. package/dist/recipeOrchestration.js +341 -24
  330. package/dist/recipeOrchestration.js.map +1 -1
  331. package/dist/recipeRoutes.d.ts +58 -2
  332. package/dist/recipeRoutes.js +744 -115
  333. package/dist/recipeRoutes.js.map +1 -1
  334. package/dist/recipes/RecipeOrchestrator.d.ts +2 -0
  335. package/dist/recipes/RecipeOrchestrator.js +6 -1
  336. package/dist/recipes/RecipeOrchestrator.js.map +1 -1
  337. package/dist/recipes/agentExecutor.d.ts +25 -5
  338. package/dist/recipes/agentExecutor.js.map +1 -1
  339. package/dist/recipes/chainedRunner.d.ts +2 -0
  340. package/dist/recipes/chainedRunner.js +142 -5
  341. package/dist/recipes/chainedRunner.js.map +1 -1
  342. package/dist/recipes/connectorPreflight.d.ts +66 -0
  343. package/dist/recipes/connectorPreflight.js +169 -0
  344. package/dist/recipes/connectorPreflight.js.map +1 -0
  345. package/dist/recipes/dependencyGraph.js +13 -5
  346. package/dist/recipes/dependencyGraph.js.map +1 -1
  347. package/dist/recipes/githubInstallSource.d.ts +128 -0
  348. package/dist/recipes/githubInstallSource.js +206 -0
  349. package/dist/recipes/githubInstallSource.js.map +1 -0
  350. package/dist/recipes/haltCategory.d.ts +119 -0
  351. package/dist/recipes/haltCategory.js +188 -0
  352. package/dist/recipes/haltCategory.js.map +1 -0
  353. package/dist/recipes/idempotencyKey.d.ts +134 -0
  354. package/dist/recipes/idempotencyKey.js +322 -0
  355. package/dist/recipes/idempotencyKey.js.map +1 -0
  356. package/dist/recipes/installer.js +48 -2
  357. package/dist/recipes/installer.js.map +1 -1
  358. package/dist/recipes/judgeSummary.d.ts +50 -0
  359. package/dist/recipes/judgeSummary.js +47 -0
  360. package/dist/recipes/judgeSummary.js.map +1 -0
  361. package/dist/recipes/judgeVerdict.d.ts +48 -0
  362. package/dist/recipes/judgeVerdict.js +174 -0
  363. package/dist/recipes/judgeVerdict.js.map +1 -0
  364. package/dist/recipes/migrations/index.d.ts +9 -0
  365. package/dist/recipes/migrations/index.js +133 -0
  366. package/dist/recipes/migrations/index.js.map +1 -1
  367. package/dist/recipes/names.d.ts +20 -0
  368. package/dist/recipes/names.js +25 -0
  369. package/dist/recipes/names.js.map +1 -1
  370. package/dist/recipes/parser.js +88 -5
  371. package/dist/recipes/parser.js.map +1 -1
  372. package/dist/recipes/replayRun.js +1 -1
  373. package/dist/recipes/replayRun.js.map +1 -1
  374. package/dist/recipes/runBudget.d.ts +70 -0
  375. package/dist/recipes/runBudget.js +109 -0
  376. package/dist/recipes/runBudget.js.map +1 -0
  377. package/dist/recipes/scheduler.d.ts +30 -0
  378. package/dist/recipes/scheduler.js +69 -19
  379. package/dist/recipes/scheduler.js.map +1 -1
  380. package/dist/recipes/schema.d.ts +36 -0
  381. package/dist/recipes/schemaGenerator.js +103 -5
  382. package/dist/recipes/schemaGenerator.js.map +1 -1
  383. package/dist/recipes/stepObservation.js +9 -0
  384. package/dist/recipes/stepObservation.js.map +1 -1
  385. package/dist/recipes/toolRegistry.js +20 -3
  386. package/dist/recipes/toolRegistry.js.map +1 -1
  387. package/dist/recipes/tools/fanOut.d.ts +20 -0
  388. package/dist/recipes/tools/fanOut.js +199 -0
  389. package/dist/recipes/tools/fanOut.js.map +1 -0
  390. package/dist/recipes/tools/file.js +5 -2
  391. package/dist/recipes/tools/file.js.map +1 -1
  392. package/dist/recipes/tools/github.d.ts +1 -1
  393. package/dist/recipes/tools/github.js +75 -1
  394. package/dist/recipes/tools/github.js.map +1 -1
  395. package/dist/recipes/tools/gmail.js +27 -5
  396. package/dist/recipes/tools/gmail.js.map +1 -1
  397. package/dist/recipes/tools/googleDrive.js +64 -0
  398. package/dist/recipes/tools/googleDrive.js.map +1 -1
  399. package/dist/recipes/tools/http.d.ts +10 -0
  400. package/dist/recipes/tools/http.js +176 -0
  401. package/dist/recipes/tools/http.js.map +1 -0
  402. package/dist/recipes/tools/index.d.ts +2 -0
  403. package/dist/recipes/tools/index.js +2 -0
  404. package/dist/recipes/tools/index.js.map +1 -1
  405. package/dist/recipes/tools/slack.js +1 -1
  406. package/dist/recipes/validation.d.ts +17 -0
  407. package/dist/recipes/validation.js +29 -11
  408. package/dist/recipes/validation.js.map +1 -1
  409. package/dist/recipes/workspaceRoot.d.ts +37 -0
  410. package/dist/recipes/workspaceRoot.js +73 -0
  411. package/dist/recipes/workspaceRoot.js.map +1 -0
  412. package/dist/recipes/yamlPositions.d.ts +56 -0
  413. package/dist/recipes/yamlPositions.js +183 -0
  414. package/dist/recipes/yamlPositions.js.map +1 -0
  415. package/dist/recipes/yamlRunner.d.ts +182 -8
  416. package/dist/recipes/yamlRunner.js +877 -217
  417. package/dist/recipes/yamlRunner.js.map +1 -1
  418. package/dist/recipesHttp.d.ts +19 -3
  419. package/dist/recipesHttp.js +67 -11
  420. package/dist/recipesHttp.js.map +1 -1
  421. package/dist/resources.js +21 -13
  422. package/dist/resources.js.map +1 -1
  423. package/dist/runLog.d.ts +58 -5
  424. package/dist/runLog.js +98 -21
  425. package/dist/runLog.js.map +1 -1
  426. package/dist/sanitizeParsedJson.d.ts +39 -0
  427. package/dist/sanitizeParsedJson.js +55 -0
  428. package/dist/sanitizeParsedJson.js.map +1 -0
  429. package/dist/schemas/recipe.v1.json +885 -196
  430. package/dist/server.d.ts +160 -3
  431. package/dist/server.js +1099 -122
  432. package/dist/server.js.map +1 -1
  433. package/dist/sessionCheckpoint.d.ts +8 -0
  434. package/dist/sessionCheckpoint.js +33 -3
  435. package/dist/sessionCheckpoint.js.map +1 -1
  436. package/dist/ssrfGuard.d.ts +16 -0
  437. package/dist/ssrfGuard.js +87 -4
  438. package/dist/ssrfGuard.js.map +1 -1
  439. package/dist/streamableHttp.d.ts +9 -4
  440. package/dist/streamableHttp.js +76 -20
  441. package/dist/streamableHttp.js.map +1 -1
  442. package/dist/telemetry.js +19 -12
  443. package/dist/telemetry.js.map +1 -1
  444. package/dist/testing/shadowRun.d.ts +52 -0
  445. package/dist/testing/shadowRun.js +71 -0
  446. package/dist/testing/shadowRun.js.map +1 -0
  447. package/dist/tools/batchLsp.d.ts +3 -0
  448. package/dist/tools/bridgeDoctor.d.ts +11 -0
  449. package/dist/tools/bridgeDoctor.js +48 -2
  450. package/dist/tools/bridgeDoctor.js.map +1 -1
  451. package/dist/tools/bridgeStatus.d.ts +0 -12
  452. package/dist/tools/bridgeStatus.js +2 -28
  453. package/dist/tools/bridgeStatus.js.map +1 -1
  454. package/dist/tools/cancelClaudeTask.d.ts +1 -0
  455. package/dist/tools/clipboard.d.ts +2 -0
  456. package/dist/tools/closeTabs.d.ts +1 -0
  457. package/dist/tools/codeLens.d.ts +1 -0
  458. package/dist/tools/createIssueFromAIComment.d.ts +1 -0
  459. package/dist/tools/ctxGetTaskContext.d.ts +9 -0
  460. package/dist/tools/ctxGetTaskContext.js +50 -2
  461. package/dist/tools/ctxGetTaskContext.js.map +1 -1
  462. package/dist/tools/ctxQueryTraces.js +32 -24
  463. package/dist/tools/ctxQueryTraces.js.map +1 -1
  464. package/dist/tools/ctxSaveTrace.d.ts +1 -0
  465. package/dist/tools/debug.d.ts +4 -0
  466. package/dist/tools/decorations.d.ts +2 -0
  467. package/dist/tools/detectUnusedCode.js +9 -7
  468. package/dist/tools/detectUnusedCode.js.map +1 -1
  469. package/dist/tools/documentLinks.d.ts +1 -0
  470. package/dist/tools/editText.d.ts +1 -0
  471. package/dist/tools/editText.js +2 -1
  472. package/dist/tools/editText.js.map +1 -1
  473. package/dist/tools/enrichCommit.d.ts +1 -0
  474. package/dist/tools/enrichStackTrace.js +11 -5
  475. package/dist/tools/enrichStackTrace.js.map +1 -1
  476. package/dist/tools/explainDiagnostic.d.ts +1 -0
  477. package/dist/tools/explainSymbol.d.ts +1 -0
  478. package/dist/tools/fileOperations.d.ts +3 -0
  479. package/dist/tools/fileOperations.js +2 -1
  480. package/dist/tools/fileOperations.js.map +1 -1
  481. package/dist/tools/fileWatcher.d.ts +2 -0
  482. package/dist/tools/fileWatcher.js +8 -2
  483. package/dist/tools/fileWatcher.js.map +1 -1
  484. package/dist/tools/findFiles.d.ts +1 -0
  485. package/dist/tools/fixAllLintErrors.d.ts +1 -0
  486. package/dist/tools/fixAllLintErrors.js +10 -5
  487. package/dist/tools/fixAllLintErrors.js.map +1 -1
  488. package/dist/tools/foldingRanges.d.ts +1 -0
  489. package/dist/tools/formatDocument.d.ts +1 -0
  490. package/dist/tools/formatDocument.js +10 -5
  491. package/dist/tools/formatDocument.js.map +1 -1
  492. package/dist/tools/generateTests.d.ts +1 -0
  493. package/dist/tools/getAIComments.d.ts +1 -0
  494. package/dist/tools/getAnalyticsReport.js +5 -1
  495. package/dist/tools/getAnalyticsReport.js.map +1 -1
  496. package/dist/tools/getBufferContent.d.ts +1 -0
  497. package/dist/tools/getChangeImpact.d.ts +1 -0
  498. package/dist/tools/getChangeImpact.js +23 -14
  499. package/dist/tools/getChangeImpact.js.map +1 -1
  500. package/dist/tools/getClaudeTaskStatus.d.ts +1 -0
  501. package/dist/tools/getCodeCoverage.d.ts +1 -0
  502. package/dist/tools/getCodeCoverage.js +32 -14
  503. package/dist/tools/getCodeCoverage.js.map +1 -1
  504. package/dist/tools/getCommitsForIssue.d.ts +1 -0
  505. package/dist/tools/getDebugState.d.ts +1 -0
  506. package/dist/tools/getDiagnostics.js +32 -29
  507. package/dist/tools/getDiagnostics.js.map +1 -1
  508. package/dist/tools/getDiffFromHandoff.js +8 -2
  509. package/dist/tools/getDiffFromHandoff.js.map +1 -1
  510. package/dist/tools/getDocumentSymbols.d.ts +1 -0
  511. package/dist/tools/getGitHotspots.d.ts +1 -0
  512. package/dist/tools/getImportedSignatures.d.ts +1 -0
  513. package/dist/tools/getImportedSignatures.js +18 -14
  514. package/dist/tools/getImportedSignatures.js.map +1 -1
  515. package/dist/tools/getPRTemplate.d.ts +1 -0
  516. package/dist/tools/getProjectContext.js +23 -10
  517. package/dist/tools/getProjectContext.js.map +1 -1
  518. package/dist/tools/getSymbolHistory.d.ts +1 -0
  519. package/dist/tools/getToolCapabilities.d.ts +10 -5
  520. package/dist/tools/getToolCapabilities.js +79 -202
  521. package/dist/tools/getToolCapabilities.js.map +1 -1
  522. package/dist/tools/getTypeSignature.d.ts +1 -0
  523. package/dist/tools/getWorkspaceSettings.d.ts +1 -0
  524. package/dist/tools/gitWrite.d.ts +11 -0
  525. package/dist/tools/github/actions.d.ts +2 -0
  526. package/dist/tools/github/composite.d.ts +3 -0
  527. package/dist/tools/github/issues.d.ts +4 -0
  528. package/dist/tools/github/pr.d.ts +7 -0
  529. package/dist/tools/handoffNote.d.ts +1 -0
  530. package/dist/tools/handoffNote.js +2 -1
  531. package/dist/tools/handoffNote.js.map +1 -1
  532. package/dist/tools/headless/lspClient.js +3 -0
  533. package/dist/tools/headless/lspClient.js.map +1 -1
  534. package/dist/tools/hoverAtCursor.d.ts +1 -0
  535. package/dist/tools/httpClient.d.ts +2 -0
  536. package/dist/tools/httpClient.js +38 -71
  537. package/dist/tools/httpClient.js.map +1 -1
  538. package/dist/tools/inlayHints.d.ts +1 -0
  539. package/dist/tools/launchQuickTask.d.ts +1 -0
  540. package/dist/tools/listClaudeTasks.d.ts +1 -0
  541. package/dist/tools/listClaudeTasks.js +10 -8
  542. package/dist/tools/listClaudeTasks.js.map +1 -1
  543. package/dist/tools/listTerminals.d.ts +1 -0
  544. package/dist/tools/lsp.d.ts +15 -0
  545. package/dist/tools/lsp.js +17 -0
  546. package/dist/tools/lsp.js.map +1 -1
  547. package/dist/tools/navigateToSymbolByName.d.ts +1 -0
  548. package/dist/tools/openDiff.d.ts +1 -0
  549. package/dist/tools/openDiff.js +4 -1
  550. package/dist/tools/openDiff.js.map +1 -1
  551. package/dist/tools/openFile.d.ts +1 -0
  552. package/dist/tools/openFile.js +4 -1
  553. package/dist/tools/openFile.js.map +1 -1
  554. package/dist/tools/openInBrowser.js +6 -1
  555. package/dist/tools/openInBrowser.js.map +1 -1
  556. package/dist/tools/organizeImports.d.ts +1 -0
  557. package/dist/tools/organizeImports.js +5 -3
  558. package/dist/tools/organizeImports.js.map +1 -1
  559. package/dist/tools/performanceReport.js +5 -3
  560. package/dist/tools/performanceReport.js.map +1 -1
  561. package/dist/tools/planPersistence.d.ts +3 -0
  562. package/dist/tools/planPersistence.js +4 -1
  563. package/dist/tools/planPersistence.js.map +1 -1
  564. package/dist/tools/previewEdit.d.ts +1 -0
  565. package/dist/tools/previewEdit.js +15 -4
  566. package/dist/tools/previewEdit.js.map +1 -1
  567. package/dist/tools/recentTracesDigest.js +54 -11
  568. package/dist/tools/recentTracesDigest.js.map +1 -1
  569. package/dist/tools/refactorAnalyze.d.ts +1 -0
  570. package/dist/tools/refactorExtractFunction.js +4 -1
  571. package/dist/tools/refactorExtractFunction.js.map +1 -1
  572. package/dist/tools/refactorPreview.d.ts +1 -0
  573. package/dist/tools/refactorPreview.js +10 -2
  574. package/dist/tools/refactorPreview.js.map +1 -1
  575. package/dist/tools/replaceBlock.d.ts +1 -0
  576. package/dist/tools/replaceBlock.js +2 -1
  577. package/dist/tools/replaceBlock.js.map +1 -1
  578. package/dist/tools/resumeClaudeTask.d.ts +1 -0
  579. package/dist/tools/runClaudeTask.d.ts +1 -0
  580. package/dist/tools/runTests.js +15 -4
  581. package/dist/tools/runTests.js.map +1 -1
  582. package/dist/tools/screenshot.d.ts +1 -0
  583. package/dist/tools/screenshotAndAnnotate.js +6 -2
  584. package/dist/tools/screenshotAndAnnotate.js.map +1 -1
  585. package/dist/tools/searchAndReplace.d.ts +1 -0
  586. package/dist/tools/searchAndReplace.js +17 -7
  587. package/dist/tools/searchAndReplace.js.map +1 -1
  588. package/dist/tools/searchTools.js +20 -19
  589. package/dist/tools/searchTools.js.map +1 -1
  590. package/dist/tools/searchWorkspace.d.ts +1 -0
  591. package/dist/tools/selectionRanges.d.ts +1 -0
  592. package/dist/tools/semanticTokens.d.ts +1 -0
  593. package/dist/tools/signatureHelp.d.ts +1 -0
  594. package/dist/tools/spawnWorkspace.js +15 -7
  595. package/dist/tools/spawnWorkspace.js.map +1 -1
  596. package/dist/tools/terminal.d.ts +6 -0
  597. package/dist/tools/testRunners/pytest.js +6 -2
  598. package/dist/tools/testRunners/pytest.js.map +1 -1
  599. package/dist/tools/testRunners/vitestJest.js +3 -1
  600. package/dist/tools/testRunners/vitestJest.js.map +1 -1
  601. package/dist/tools/testTraceToSource.d.ts +1 -0
  602. package/dist/tools/transaction.d.ts +4 -0
  603. package/dist/tools/transaction.js +4 -1
  604. package/dist/tools/transaction.js.map +1 -1
  605. package/dist/tools/typeHierarchy.d.ts +1 -0
  606. package/dist/tools/utils.d.ts +18 -0
  607. package/dist/tools/utils.js +103 -18
  608. package/dist/tools/utils.js.map +1 -1
  609. package/dist/tools/vscodeCommands.d.ts +2 -0
  610. package/dist/tools/vscodeTasks.d.ts +2 -0
  611. package/dist/tools/workspaceSettings.d.ts +1 -0
  612. package/dist/transport.d.ts +3 -1
  613. package/dist/transport.js +103 -52
  614. package/dist/transport.js.map +1 -1
  615. package/dist/winShim.d.ts +34 -0
  616. package/dist/winShim.js +94 -0
  617. package/dist/winShim.js.map +1 -0
  618. package/dist/wireHaltPushDispatch.d.ts +38 -0
  619. package/dist/wireHaltPushDispatch.js +71 -0
  620. package/dist/wireHaltPushDispatch.js.map +1 -0
  621. package/dist/writeFileAtomic.d.ts +23 -0
  622. package/dist/writeFileAtomic.js +121 -0
  623. package/dist/writeFileAtomic.js.map +1 -0
  624. package/package.json +23 -7
  625. package/scripts/postinstall.mjs +42 -2
  626. package/scripts/smoke/run-all.mjs +213 -0
  627. package/scripts/start-all.mjs +572 -0
  628. package/scripts/start-all.ps1 +209 -0
  629. package/scripts/start-all.sh +73 -17
  630. package/scripts/start-orchestrator.ps1 +158 -0
  631. package/scripts/start-remote.mjs +122 -0
  632. package/templates/automation-policies/recipe-authoring.json +1 -1
  633. package/templates/automation-policies/security-first.json +1 -1
  634. package/templates/automation-policies/strict-lint.json +1 -1
  635. package/templates/automation-policies/test-driven.json +1 -1
  636. package/templates/automation-policy.example.json +1 -1
  637. package/templates/co.patchwork-os.bridge.plist +2 -2
  638. package/templates/recipes/approval-queue-ui-test.yaml +1 -1
  639. package/templates/recipes/ctx-loop-test.yaml +1 -1
  640. package/templates/recipes/fix-errors-on-save.yaml +71 -0
  641. package/templates/recipes/morning-brief.yaml +5 -2
  642. package/templates/recipes/project-health-check.yaml +4 -1
  643. package/templates/recipes/sentry-to-linear.yaml +72 -38
  644. package/templates/recipes/webhook/apple-watch-health-log.yaml +145 -0
  645. package/templates/recipes/webhook/customer-escalation.yaml +8 -9
  646. package/templates/recipes/webhook/meeting-prep.yaml +11 -5
  647. package/dist/commands/marketplace.d.ts +0 -16
  648. package/dist/commands/marketplace.js +0 -32
  649. package/dist/commands/marketplace.js.map +0 -1
  650. package/dist/recipes/legacyRecipeCompat.d.ts +0 -10
  651. package/dist/recipes/legacyRecipeCompat.js +0 -131
  652. package/dist/recipes/legacyRecipeCompat.js.map +0 -1
@@ -0,0 +1,700 @@
1
+ /**
2
+ * Airtable connector — record access via the Airtable REST API v0.
3
+ *
4
+ * Auth: Personal Access Token (PAT) or OAuth 2.0.
5
+ * PAT: `Authorization: Bearer <pat>`. PATs start with `pat...`.
6
+ * - Env var: AIRTABLE_ACCESS_TOKEN
7
+ * - Stored: getSecretJsonSync("airtable") → AirtableTokens
8
+ * OAuth 2.0:
9
+ * - Env vars: AIRTABLE_CLIENT_ID, AIRTABLE_CLIENT_SECRET
10
+ * - Token endpoint: https://airtable.com/oauth2/v1/token (HTTP Basic auth)
11
+ * - Stored: getSecretJsonSync("airtable") → AirtableOAuthTokens
12
+ *
13
+ * Tools: listBases, getBaseSchema, listRecords, getRecord, createRecord,
14
+ * updateRecord, listWebhooks, createWebhook, deleteWebhook,
15
+ * getWebhookPayloads, refreshWebhook.
16
+ *
17
+ * Extends BaseConnector for unified auth, retry, rate-limit, error handling.
18
+ */
19
+ import crypto from "node:crypto";
20
+ import { BaseConnector, } from "./baseConnector.js";
21
+ import { deleteSecretJsonSync, getSecretJsonSync, storeSecretJsonSync, } from "./tokenStorage.js";
22
+ const BASE_URL = "https://api.airtable.com";
23
+ const OAUTH_TOKEN_ENDPOINT = "https://airtable.com/oauth2/v1/token";
24
+ const OAUTH_SCOPES = [
25
+ "data.records:read",
26
+ "data.records:write",
27
+ "schema.bases:read",
28
+ "webhook:manage",
29
+ ];
30
+ const MAX_RECORDS_HARD_CAP = 1000;
31
+ export class AirtableConnector extends BaseConnector {
32
+ providerName = "airtable";
33
+ tokens = null;
34
+ getOAuthConfig() {
35
+ const clientId = process.env.AIRTABLE_CLIENT_ID;
36
+ if (!clientId)
37
+ return null;
38
+ return {
39
+ clientId,
40
+ clientSecret: process.env.AIRTABLE_CLIENT_SECRET,
41
+ tokenEndpoint: OAUTH_TOKEN_ENDPOINT,
42
+ scopes: OAUTH_SCOPES,
43
+ };
44
+ }
45
+ async authenticate() {
46
+ const raw = loadRawTokens();
47
+ if (!raw) {
48
+ throw new Error("Airtable not connected. Run: patchwork-os connect airtable or set AIRTABLE_ACCESS_TOKEN");
49
+ }
50
+ if (isOAuthTokens(raw)) {
51
+ // OAuth path — may auto-refresh if expired
52
+ const expiresAt = new Date(raw.expiresAt);
53
+ const bufferMs = 5 * 60 * 1000;
54
+ if (Date.now() > expiresAt.getTime() - bufferMs && raw.refreshToken) {
55
+ const refreshed = await this._oauthRefresh(raw);
56
+ if (refreshed) {
57
+ return {
58
+ token: refreshed.accessToken,
59
+ refreshToken: refreshed.refreshToken,
60
+ expiresAt: new Date(refreshed.expiresAt),
61
+ scopes: refreshed.scopes,
62
+ };
63
+ }
64
+ }
65
+ return {
66
+ token: raw.accessToken,
67
+ refreshToken: raw.refreshToken,
68
+ expiresAt: new Date(raw.expiresAt),
69
+ scopes: raw.scopes,
70
+ };
71
+ }
72
+ // PAT path
73
+ this.tokens = raw;
74
+ return {
75
+ token: raw.accessToken,
76
+ scopes: ["read", "write"],
77
+ };
78
+ }
79
+ /**
80
+ * Override base refreshToken() to use HTTP Basic auth for Airtable's token
81
+ * endpoint (clientId:clientSecret as Basic auth instead of body params).
82
+ */
83
+ async refreshToken() {
84
+ const raw = loadRawTokens();
85
+ if (!raw || !isOAuthTokens(raw) || !raw.refreshToken)
86
+ return null;
87
+ const config = this.getOAuthConfig();
88
+ if (!config?.clientSecret)
89
+ return null;
90
+ const refreshed = await this._oauthRefresh(raw);
91
+ if (!refreshed)
92
+ return null;
93
+ return {
94
+ token: refreshed.accessToken,
95
+ refreshToken: refreshed.refreshToken,
96
+ expiresAt: new Date(refreshed.expiresAt),
97
+ scopes: refreshed.scopes,
98
+ };
99
+ }
100
+ /**
101
+ * Perform the actual Airtable OAuth token refresh using HTTP Basic auth.
102
+ * Airtable requires clientId:clientSecret as Basic auth on the token endpoint.
103
+ */
104
+ async _oauthRefresh(tokens) {
105
+ const clientId = process.env.AIRTABLE_CLIENT_ID ?? "";
106
+ const clientSecret = process.env.AIRTABLE_CLIENT_SECRET;
107
+ if (!clientId || !clientSecret)
108
+ return null;
109
+ const basicCreds = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
110
+ let response;
111
+ try {
112
+ response = await fetch(OAUTH_TOKEN_ENDPOINT, {
113
+ method: "POST",
114
+ headers: {
115
+ "Content-Type": "application/x-www-form-urlencoded",
116
+ Authorization: `Basic ${basicCreds}`,
117
+ },
118
+ body: new URLSearchParams({
119
+ grant_type: "refresh_token",
120
+ refresh_token: tokens.refreshToken,
121
+ }).toString(),
122
+ });
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ if (!response.ok) {
128
+ return null;
129
+ }
130
+ let data;
131
+ try {
132
+ data = (await response.json());
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ if (typeof data.access_token !== "string" || !data.access_token) {
138
+ return null;
139
+ }
140
+ const expiresIn = typeof data.expires_in === "number" && data.expires_in > 0
141
+ ? data.expires_in
142
+ : 3600;
143
+ const updated = {
144
+ ...tokens,
145
+ accessToken: data.access_token,
146
+ refreshToken: typeof data.refresh_token === "string" && data.refresh_token
147
+ ? data.refresh_token
148
+ : tokens.refreshToken,
149
+ expiresAt: new Date(Date.now() + expiresIn * 1000).toISOString(),
150
+ scopes: typeof data.scope === "string" ? data.scope.split(" ") : tokens.scopes,
151
+ };
152
+ saveOAuthTokens(updated);
153
+ return updated;
154
+ }
155
+ async healthCheck() {
156
+ try {
157
+ const result = await this.apiCall(async () => {
158
+ const res = await fetch(`${BASE_URL}/v0/meta/whoami`, {
159
+ headers: this.buildHeaders(),
160
+ });
161
+ if (!res.ok)
162
+ throw res;
163
+ return res.json();
164
+ });
165
+ if ("error" in result)
166
+ return { ok: false, error: result.error };
167
+ return { ok: true };
168
+ }
169
+ catch (err) {
170
+ return { ok: false, error: this.normalizeError(err) };
171
+ }
172
+ }
173
+ normalizeError(error) {
174
+ if (error instanceof Response) {
175
+ const s = error.status;
176
+ if (s === 401)
177
+ return {
178
+ code: "auth_expired",
179
+ message: "Airtable authentication expired — reconnect",
180
+ retryable: false,
181
+ suggestedAction: "patchwork-os connect airtable",
182
+ };
183
+ if (s === 403)
184
+ return {
185
+ code: "permission_denied",
186
+ message: "Insufficient Airtable permissions for this base/table",
187
+ retryable: false,
188
+ };
189
+ if (s === 404)
190
+ return {
191
+ code: "not_found",
192
+ message: "Airtable resource not found",
193
+ retryable: false,
194
+ };
195
+ if (s === 422)
196
+ return {
197
+ code: "provider_error",
198
+ message: "Airtable validation error (invalid fields or formula)",
199
+ retryable: false,
200
+ };
201
+ if (s === 429)
202
+ return {
203
+ code: "rate_limited",
204
+ message: "Airtable API rate limit exceeded",
205
+ retryable: true,
206
+ suggestedAction: "Wait and retry",
207
+ };
208
+ return {
209
+ code: "provider_error",
210
+ message: `Airtable API error: HTTP ${s}`,
211
+ retryable: s >= 500,
212
+ };
213
+ }
214
+ if (error instanceof Error) {
215
+ if (error.message.includes("ENOTFOUND") ||
216
+ error.message.includes("ECONNREFUSED")) {
217
+ return {
218
+ code: "network_error",
219
+ message: `Cannot connect to Airtable: ${error.message}`,
220
+ retryable: true,
221
+ };
222
+ }
223
+ }
224
+ return {
225
+ code: "provider_error",
226
+ message: error instanceof Error ? error.message : String(error),
227
+ retryable: false,
228
+ };
229
+ }
230
+ getStatus() {
231
+ const raw = loadRawTokens();
232
+ if (!raw) {
233
+ return { id: "airtable", status: "disconnected" };
234
+ }
235
+ if (isOAuthTokens(raw)) {
236
+ return {
237
+ id: "airtable",
238
+ status: "connected",
239
+ lastSync: raw.connected_at,
240
+ };
241
+ }
242
+ const tokens = raw;
243
+ return {
244
+ id: "airtable",
245
+ status: "connected",
246
+ lastSync: tokens.connected_at,
247
+ workspace: tokens.email
248
+ ? `Airtable (${tokens.email})`
249
+ : tokens.userId
250
+ ? `Airtable user ${tokens.userId}`
251
+ : undefined,
252
+ };
253
+ }
254
+ // ── API Methods ────────────────────────────────────────────────────────────
255
+ async listBases() {
256
+ const result = await this.apiCall(async () => {
257
+ const res = await fetch(`${BASE_URL}/v0/meta/bases`, {
258
+ headers: this.buildHeaders(),
259
+ });
260
+ if (!res.ok)
261
+ throw res;
262
+ return res.json();
263
+ });
264
+ if ("error" in result)
265
+ throw new Error(result.error.message);
266
+ return result.data;
267
+ }
268
+ async getBaseSchema(baseId) {
269
+ const result = await this.apiCall(async () => {
270
+ const res = await fetch(`${BASE_URL}/v0/meta/bases/${encodeURIComponent(baseId)}/tables`, { headers: this.buildHeaders() });
271
+ if (!res.ok)
272
+ throw res;
273
+ return res.json();
274
+ });
275
+ if ("error" in result)
276
+ throw new Error(result.error.message);
277
+ return result.data;
278
+ }
279
+ async listRecords(baseId, tableIdOrName, params = {}) {
280
+ const result = await this.apiCall(async () => {
281
+ const qs = new URLSearchParams();
282
+ if (params.filterByFormula)
283
+ qs.set("filterByFormula", params.filterByFormula);
284
+ if (params.view)
285
+ qs.set("view", params.view);
286
+ if (params.pageSize)
287
+ qs.set("pageSize", String(params.pageSize));
288
+ // Cap maxRecords at Airtable's 1000 hard limit. Default 100.
289
+ const maxRecords = Math.min(params.maxRecords ?? 100, MAX_RECORDS_HARD_CAP);
290
+ qs.set("maxRecords", String(maxRecords));
291
+ if (params.sort) {
292
+ params.sort.forEach((s, i) => {
293
+ qs.set(`sort[${i}][field]`, s.field);
294
+ if (s.direction)
295
+ qs.set(`sort[${i}][direction]`, s.direction);
296
+ });
297
+ }
298
+ if (params.fields) {
299
+ for (const f of params.fields)
300
+ qs.append("fields[]", f);
301
+ }
302
+ const url = `${BASE_URL}/v0/${encodeURIComponent(baseId)}/${encodeURIComponent(tableIdOrName)}?${qs}`;
303
+ const res = await fetch(url, { headers: this.buildHeaders() });
304
+ if (!res.ok)
305
+ throw res;
306
+ return res.json();
307
+ });
308
+ if ("error" in result)
309
+ throw new Error(result.error.message);
310
+ return result.data;
311
+ }
312
+ async getRecord(baseId, tableIdOrName, recordId) {
313
+ const result = await this.apiCall(async () => {
314
+ const url = `${BASE_URL}/v0/${encodeURIComponent(baseId)}/${encodeURIComponent(tableIdOrName)}/${encodeURIComponent(recordId)}`;
315
+ const res = await fetch(url, { headers: this.buildHeaders() });
316
+ if (!res.ok)
317
+ throw res;
318
+ return res.json();
319
+ });
320
+ if ("error" in result)
321
+ throw new Error(result.error.message);
322
+ return result.data;
323
+ }
324
+ async createRecord(baseId, tableIdOrName, fields) {
325
+ const result = await this.apiCall(async () => {
326
+ const url = `${BASE_URL}/v0/${encodeURIComponent(baseId)}/${encodeURIComponent(tableIdOrName)}`;
327
+ const res = await fetch(url, {
328
+ method: "POST",
329
+ headers: {
330
+ ...this.buildHeaders(),
331
+ "Content-Type": "application/json",
332
+ },
333
+ body: JSON.stringify({ records: [{ fields }] }),
334
+ });
335
+ if (!res.ok)
336
+ throw res;
337
+ const json = (await res.json());
338
+ const first = json.records?.[0];
339
+ if (!first)
340
+ throw new Error("Airtable returned no record in response");
341
+ return first;
342
+ });
343
+ if ("error" in result)
344
+ throw new Error(result.error.message);
345
+ return result.data;
346
+ }
347
+ async updateRecord(baseId, tableIdOrName, recordId, fields) {
348
+ const result = await this.apiCall(async () => {
349
+ const url = `${BASE_URL}/v0/${encodeURIComponent(baseId)}/${encodeURIComponent(tableIdOrName)}/${encodeURIComponent(recordId)}`;
350
+ const res = await fetch(url, {
351
+ method: "PATCH",
352
+ headers: {
353
+ ...this.buildHeaders(),
354
+ "Content-Type": "application/json",
355
+ },
356
+ body: JSON.stringify({ fields }),
357
+ });
358
+ if (!res.ok)
359
+ throw res;
360
+ return res.json();
361
+ });
362
+ if ("error" in result)
363
+ throw new Error(result.error.message);
364
+ return result.data;
365
+ }
366
+ // ── Webhook Methods ────────────────────────────────────────────────────────
367
+ /** GET /v0/bases/{baseId}/webhooks */
368
+ async listWebhooks(baseId) {
369
+ const result = await this.apiCall(async () => {
370
+ const url = `${BASE_URL}/v0/bases/${encodeURIComponent(baseId)}/webhooks`;
371
+ const res = await fetch(url, { headers: this.buildHeaders() });
372
+ if (!res.ok)
373
+ throw res;
374
+ const json = (await res.json());
375
+ return json.webhooks ?? [];
376
+ });
377
+ if ("error" in result)
378
+ throw new Error(result.error.message);
379
+ return result.data;
380
+ }
381
+ /**
382
+ * POST /v0/bases/{baseId}/webhooks
383
+ * Creates a webhook. Returns the webhook id, HMAC secret (base64), and
384
+ * expiration time. Store macSecretBase64 securely — it is only returned once.
385
+ */
386
+ async createWebhook(baseId, notificationUrl, specification) {
387
+ const result = await this.apiCall(async () => {
388
+ const url = `${BASE_URL}/v0/bases/${encodeURIComponent(baseId)}/webhooks`;
389
+ const body = {
390
+ notificationUrl,
391
+ specification: {
392
+ filters: specification?.filters ?? {
393
+ fromSources: ["client"],
394
+ dataTypes: ["tableData", "tableFields", "tableMetadata"],
395
+ changeTypes: ["add", "update", "remove"],
396
+ },
397
+ },
398
+ };
399
+ const res = await fetch(url, {
400
+ method: "POST",
401
+ headers: { ...this.buildHeaders(), "Content-Type": "application/json" },
402
+ body: JSON.stringify(body),
403
+ });
404
+ if (!res.ok)
405
+ throw res;
406
+ return res.json();
407
+ });
408
+ if ("error" in result)
409
+ throw new Error(result.error.message);
410
+ return result.data;
411
+ }
412
+ /** DELETE /v0/bases/{baseId}/webhooks/{webhookId} */
413
+ async deleteWebhook(baseId, webhookId) {
414
+ const result = await this.apiCall(async () => {
415
+ const url = `${BASE_URL}/v0/bases/${encodeURIComponent(baseId)}/webhooks/${encodeURIComponent(webhookId)}`;
416
+ const res = await fetch(url, {
417
+ method: "DELETE",
418
+ headers: this.buildHeaders(),
419
+ });
420
+ if (!res.ok)
421
+ throw res;
422
+ return undefined;
423
+ });
424
+ if ("error" in result)
425
+ throw new Error(result.error.message);
426
+ }
427
+ /**
428
+ * GET /v0/bases/{baseId}/webhooks/{webhookId}/payloads
429
+ * Retrieves queued payloads for a webhook. Pass cursor from previous call
430
+ * to page through results.
431
+ */
432
+ async getWebhookPayloads(baseId, webhookId, cursor) {
433
+ const result = await this.apiCall(async () => {
434
+ const qs = cursor != null ? `?cursor=${cursor}` : "";
435
+ const url = `${BASE_URL}/v0/bases/${encodeURIComponent(baseId)}/webhooks/${encodeURIComponent(webhookId)}/payloads${qs}`;
436
+ const res = await fetch(url, { headers: this.buildHeaders() });
437
+ if (!res.ok)
438
+ throw res;
439
+ return res.json();
440
+ });
441
+ if ("error" in result)
442
+ throw new Error(result.error.message);
443
+ return result.data;
444
+ }
445
+ /**
446
+ * POST /v0/bases/{baseId}/webhooks/{webhookId}/refresh
447
+ * Extends the webhook expiry by 7 days from now.
448
+ */
449
+ async refreshWebhook(baseId, webhookId) {
450
+ const result = await this.apiCall(async () => {
451
+ const url = `${BASE_URL}/v0/bases/${encodeURIComponent(baseId)}/webhooks/${encodeURIComponent(webhookId)}/refresh`;
452
+ const res = await fetch(url, {
453
+ method: "POST",
454
+ headers: { ...this.buildHeaders(), "Content-Type": "application/json" },
455
+ body: "{}",
456
+ });
457
+ if (!res.ok)
458
+ throw res;
459
+ return undefined;
460
+ });
461
+ if ("error" in result)
462
+ throw new Error(result.error.message);
463
+ }
464
+ // ── Helpers ────────────────────────────────────────────────────────────────
465
+ buildHeaders() {
466
+ // Prefer the in-memory auth token (set after authenticate()), then fall
467
+ // back to reading directly from storage for callers that haven't gone
468
+ // through the full authenticate() flow (e.g. healthCheck).
469
+ const token = this.auth?.token ??
470
+ this.tokens?.accessToken ??
471
+ loadRawTokens()?.accessToken ??
472
+ "";
473
+ return {
474
+ Authorization: `Bearer ${token}`,
475
+ Accept: "application/json",
476
+ };
477
+ }
478
+ }
479
+ // ── Token persistence ────────────────────────────────────────────────────────
480
+ /** Discriminator for OAuth vs PAT token blobs. */
481
+ function isOAuthTokens(raw) {
482
+ return raw._oauth === true;
483
+ }
484
+ /**
485
+ * Load raw token blob from env / storage. Returns either AirtableTokens (PAT)
486
+ * or AirtableOAuthTokens (OAuth), or null when not connected.
487
+ *
488
+ * Priority:
489
+ * 1. AIRTABLE_ACCESS_TOKEN env var → synthetic PAT tokens
490
+ * 2. Stored token blob (may be PAT or OAuth)
491
+ */
492
+ export function loadRawTokens() {
493
+ const envKey = process.env.AIRTABLE_ACCESS_TOKEN;
494
+ if (envKey) {
495
+ return {
496
+ accessToken: envKey,
497
+ connected_at: new Date().toISOString(),
498
+ };
499
+ }
500
+ return getSecretJsonSync("airtable");
501
+ }
502
+ /**
503
+ * loadTokens returns PAT tokens for backward compatibility with existing code
504
+ * that expects AirtableTokens. Returns null for OAuth-only connections.
505
+ */
506
+ export function loadTokens() {
507
+ const raw = loadRawTokens();
508
+ if (!raw)
509
+ return null;
510
+ if (isOAuthTokens(raw)) {
511
+ // Return a minimal AirtableTokens shape for backward-compat callers that
512
+ // only need the accessToken (e.g. getStatus, healthCheck).
513
+ return {
514
+ accessToken: raw.accessToken,
515
+ connected_at: raw.connected_at,
516
+ };
517
+ }
518
+ return raw;
519
+ }
520
+ export function saveTokens(tokens) {
521
+ storeSecretJsonSync("airtable", tokens);
522
+ }
523
+ export function saveOAuthTokens(tokens) {
524
+ storeSecretJsonSync("airtable", tokens);
525
+ }
526
+ export function clearTokens() {
527
+ try {
528
+ deleteSecretJsonSync("airtable");
529
+ }
530
+ catch {
531
+ // ignore
532
+ }
533
+ }
534
+ // ── Webhook verification ─────────────────────────────────────────────────────
535
+ /**
536
+ * Verify an incoming Airtable webhook request.
537
+ *
538
+ * Airtable signs webhook payloads with HMAC-SHA256 using the macSecretBase64
539
+ * returned at webhook creation time. The signature is hex-encoded and sent in
540
+ * the `X-Airtable-Content-MAC` header. The raw body (before JSON parsing) is
541
+ * used as the message.
542
+ *
543
+ * Additionally, `X-Airtable-Client-Secret` should equal the base64-decoded
544
+ * mac secret (raw bytes as a Latin-1/binary string) — but in practice most
545
+ * integrations only check the HMAC signature. This helper validates both.
546
+ *
547
+ * @param rawBody - Raw request body as a Buffer or string
548
+ * @param hmacHeader - Value of the `X-Airtable-Content-MAC` header (hex)
549
+ * @param macSecretBase64 - The macSecretBase64 returned by createWebhook()
550
+ * @returns true if the signature is valid
551
+ */
552
+ export function verifyAirtableWebhook(rawBody, hmacHeader, macSecretBase64) {
553
+ const secret = Buffer.from(macSecretBase64, "base64");
554
+ const body = typeof rawBody === "string" ? Buffer.from(rawBody, "utf-8") : rawBody;
555
+ const expected = crypto
556
+ .createHmac("sha256", secret)
557
+ .update(body)
558
+ .digest("hex");
559
+ // Constant-time compare to prevent timing attacks
560
+ try {
561
+ const expectedBuf = Buffer.from(expected, "utf-8");
562
+ const actualBuf = Buffer.from(hmacHeader, "utf-8");
563
+ if (expectedBuf.length !== actualBuf.length)
564
+ return false;
565
+ return crypto.timingSafeEqual(expectedBuf, actualBuf);
566
+ }
567
+ catch {
568
+ return false;
569
+ }
570
+ }
571
+ // ── Singleton instance ───────────────────────────────────────────────────────
572
+ let _instance = null;
573
+ function resetAirtableConnector() {
574
+ _instance = null;
575
+ }
576
+ export function getAirtableConnector() {
577
+ if (!_instance) {
578
+ _instance = new AirtableConnector();
579
+ }
580
+ return _instance;
581
+ }
582
+ export { getAirtableConnector as airtable };
583
+ /**
584
+ * POST /connections/airtable/connect { accessToken }
585
+ */
586
+ export async function handleAirtableConnect(body) {
587
+ let accessToken;
588
+ try {
589
+ const parsed = JSON.parse(body);
590
+ if (typeof parsed.accessToken !== "string" || !parsed.accessToken) {
591
+ return {
592
+ status: 400,
593
+ contentType: "application/json",
594
+ body: JSON.stringify({ ok: false, error: "accessToken is required" }),
595
+ };
596
+ }
597
+ accessToken = parsed.accessToken;
598
+ }
599
+ catch {
600
+ return {
601
+ status: 400,
602
+ contentType: "application/json",
603
+ body: JSON.stringify({ ok: false, error: "Invalid JSON body" }),
604
+ };
605
+ }
606
+ try {
607
+ const res = await fetch(`${BASE_URL}/v0/meta/whoami`, {
608
+ headers: {
609
+ Authorization: `Bearer ${accessToken}`,
610
+ Accept: "application/json",
611
+ },
612
+ });
613
+ if (!res.ok) {
614
+ return {
615
+ status: 401,
616
+ contentType: "application/json",
617
+ body: JSON.stringify({
618
+ ok: false,
619
+ error: `Credentials rejected by Airtable (HTTP ${res.status}) — check accessToken`,
620
+ }),
621
+ };
622
+ }
623
+ const who = (await res.json());
624
+ const userId = who.id ?? undefined;
625
+ const email = who.email ?? undefined;
626
+ const tokens = {
627
+ accessToken,
628
+ userId,
629
+ email,
630
+ connected_at: new Date().toISOString(),
631
+ };
632
+ saveTokens(tokens);
633
+ resetAirtableConnector();
634
+ return {
635
+ status: 200,
636
+ contentType: "application/json",
637
+ body: JSON.stringify({
638
+ ok: true,
639
+ userId,
640
+ email,
641
+ connectedAt: tokens.connected_at,
642
+ }),
643
+ };
644
+ }
645
+ catch (err) {
646
+ return {
647
+ status: 500,
648
+ contentType: "application/json",
649
+ body: JSON.stringify({
650
+ ok: false,
651
+ error: err instanceof Error ? err.message : String(err),
652
+ }),
653
+ };
654
+ }
655
+ }
656
+ /**
657
+ * POST /connections/airtable/test
658
+ */
659
+ export async function handleAirtableTest() {
660
+ const tokens = loadTokens();
661
+ if (!tokens) {
662
+ return {
663
+ status: 400,
664
+ contentType: "application/json",
665
+ body: JSON.stringify({ ok: false, error: "Airtable not connected" }),
666
+ };
667
+ }
668
+ try {
669
+ const connector = getAirtableConnector();
670
+ const check = await connector.healthCheck();
671
+ return {
672
+ status: check.ok ? 200 : 401,
673
+ contentType: "application/json",
674
+ body: JSON.stringify(check.ok ? { ok: true } : { ok: false, error: check.error?.message }),
675
+ };
676
+ }
677
+ catch (err) {
678
+ return {
679
+ status: 500,
680
+ contentType: "application/json",
681
+ body: JSON.stringify({
682
+ ok: false,
683
+ error: err instanceof Error ? err.message : String(err),
684
+ }),
685
+ };
686
+ }
687
+ }
688
+ /**
689
+ * DELETE /connections/airtable
690
+ */
691
+ export function handleAirtableDisconnect() {
692
+ clearTokens();
693
+ resetAirtableConnector();
694
+ return {
695
+ status: 200,
696
+ contentType: "application/json",
697
+ body: JSON.stringify({ ok: true }),
698
+ };
699
+ }
700
+ //# sourceMappingURL=airtable.js.map