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.
- package/README.bridge.md +14 -14
- package/README.md +201 -34
- package/deploy/README.md +1 -1
- package/deploy/bootstrap-vps.sh +14 -9
- package/deploy/deploy-dashboard.sh +25 -1
- package/deploy/macos/README.md +153 -0
- package/deploy/macos/com.patchwork.bridge.plist.template +54 -0
- package/deploy/macos/com.patchwork.tunnel.plist.template +76 -0
- package/deploy/macos/install-mac-bridge.sh +244 -0
- package/deploy/macos/uninstall-mac-bridge.sh +22 -0
- package/dist/activityLog.d.ts +6 -0
- package/dist/activityLog.js +63 -26
- package/dist/activityLog.js.map +1 -1
- package/dist/adapters/claude.js +22 -16
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/gemini.js +15 -11
- package/dist/adapters/gemini.js.map +1 -1
- package/dist/adapters/openai.js +9 -9
- package/dist/adapters/openai.js.map +1 -1
- package/dist/ajv2020.d.ts +25 -0
- package/dist/ajv2020.js +33 -0
- package/dist/ajv2020.js.map +1 -0
- package/dist/analyticsAggregator.js +1 -1
- package/dist/analyticsAggregator.js.map +1 -1
- package/dist/analyticsConfig.d.ts +29 -0
- package/dist/analyticsConfig.js +102 -0
- package/dist/analyticsConfig.js.map +1 -0
- package/dist/analyticsPrefs.d.ts +38 -2
- package/dist/analyticsPrefs.js +148 -23
- package/dist/analyticsPrefs.js.map +1 -1
- package/dist/analyticsSend.d.ts +17 -1
- package/dist/analyticsSend.js +67 -5
- package/dist/analyticsSend.js.map +1 -1
- package/dist/approvalHttp.d.ts +14 -0
- package/dist/approvalHttp.js +212 -9
- package/dist/approvalHttp.js.map +1 -1
- package/dist/approvalInsights.js +13 -5
- package/dist/approvalInsights.js.map +1 -1
- package/dist/approvalQueue.d.ts +97 -3
- package/dist/approvalQueue.js +193 -19
- package/dist/approvalQueue.js.map +1 -1
- package/dist/approvalSignals.js +19 -11
- package/dist/approvalSignals.js.map +1 -1
- package/dist/automation.d.ts +35 -10
- package/dist/automation.js +133 -37
- package/dist/automation.js.map +1 -1
- package/dist/automationSuggestions.js +10 -8
- package/dist/automationSuggestions.js.map +1 -1
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.js +202 -25
- package/dist/bridge.js.map +1 -1
- package/dist/bridgeLockDiscovery.d.ts +27 -1
- package/dist/bridgeLockDiscovery.js +38 -11
- package/dist/bridgeLockDiscovery.js.map +1 -1
- package/dist/bridgeToken.js +3 -2
- package/dist/bridgeToken.js.map +1 -1
- package/dist/claudeDriver.js +23 -8
- package/dist/claudeDriver.js.map +1 -1
- package/dist/claudeOrchestrator.d.ts +1 -1
- package/dist/claudeOrchestrator.js +87 -44
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/commands/analytics.d.ts +8 -0
- package/dist/commands/analytics.js +134 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/commands/auditEnv.d.ts +36 -0
- package/dist/commands/auditEnv.js +202 -0
- package/dist/commands/auditEnv.js.map +1 -0
- package/dist/commands/dashboard.js +8 -1
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/doctor.d.ts +35 -0
- package/dist/commands/doctor.js +51 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/install.js +3 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/launchd.js +15 -16
- package/dist/commands/launchd.js.map +1 -1
- package/dist/commands/patchworkInit.d.ts +5 -0
- package/dist/commands/patchworkInit.js +89 -7
- package/dist/commands/patchworkInit.js.map +1 -1
- package/dist/commands/recipe.d.ts +110 -0
- package/dist/commands/recipe.js +512 -9
- package/dist/commands/recipe.js.map +1 -1
- package/dist/commands/recipeInstall.js +11 -4
- package/dist/commands/recipeInstall.js.map +1 -1
- package/dist/commands/shadowScan.d.ts +34 -0
- package/dist/commands/shadowScan.js +142 -0
- package/dist/commands/shadowScan.js.map +1 -0
- package/dist/commands/task.js +2 -2
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/tools.d.ts +20 -1
- package/dist/commands/tools.js +112 -3
- package/dist/commands/tools.js.map +1 -1
- package/dist/commands/tracesImport.js +21 -4
- package/dist/commands/tracesImport.js.map +1 -1
- package/dist/commitIssueLinkLog.d.ts +16 -0
- package/dist/commitIssueLinkLog.js +110 -24
- package/dist/commitIssueLinkLog.js.map +1 -1
- package/dist/companions/registry.js +15 -7
- package/dist/companions/registry.js.map +1 -1
- package/dist/config.d.ts +47 -3
- package/dist/config.js +111 -21
- package/dist/config.js.map +1 -1
- package/dist/connectorRoutes.js +866 -57
- package/dist/connectorRoutes.js.map +1 -1
- package/dist/connectors/airtable.d.ts +230 -0
- package/dist/connectors/airtable.js +700 -0
- package/dist/connectors/airtable.js.map +1 -0
- package/dist/connectors/asana.js +13 -9
- package/dist/connectors/asana.js.map +1 -1
- package/dist/connectors/baseConnector.js +25 -3
- package/dist/connectors/baseConnector.js.map +1 -1
- package/dist/connectors/caldiy.d.ts +137 -0
- package/dist/connectors/caldiy.js +424 -0
- package/dist/connectors/caldiy.js.map +1 -0
- package/dist/connectors/circleci.d.ts +162 -0
- package/dist/connectors/circleci.js +576 -0
- package/dist/connectors/circleci.js.map +1 -0
- package/dist/connectors/cloudflare.d.ts +132 -0
- package/dist/connectors/cloudflare.js +622 -0
- package/dist/connectors/cloudflare.js.map +1 -0
- package/dist/connectors/confluence.js +35 -0
- package/dist/connectors/confluence.js.map +1 -1
- package/dist/connectors/connectorRedirectUri.d.ts +30 -0
- package/dist/connectors/connectorRedirectUri.js +38 -0
- package/dist/connectors/connectorRedirectUri.js.map +1 -0
- package/dist/connectors/connectorRegistry.d.ts +63 -0
- package/dist/connectors/connectorRegistry.js +354 -0
- package/dist/connectors/connectorRegistry.js.map +1 -0
- package/dist/connectors/datadog.js +33 -4
- package/dist/connectors/datadog.js.map +1 -1
- package/dist/connectors/discord.js +14 -10
- package/dist/connectors/discord.js.map +1 -1
- package/dist/connectors/elasticsearch.d.ts +141 -0
- package/dist/connectors/elasticsearch.js +601 -0
- package/dist/connectors/elasticsearch.js.map +1 -0
- package/dist/connectors/figma.d.ts +130 -0
- package/dist/connectors/figma.js +387 -0
- package/dist/connectors/figma.js.map +1 -0
- package/dist/connectors/github.d.ts +17 -0
- package/dist/connectors/github.js +53 -2
- package/dist/connectors/github.js.map +1 -1
- package/dist/connectors/gitlab.js +12 -6
- package/dist/connectors/gitlab.js.map +1 -1
- package/dist/connectors/gmail.js +72 -21
- package/dist/connectors/gmail.js.map +1 -1
- package/dist/connectors/googleCalendar.js +8 -8
- package/dist/connectors/googleCalendar.js.map +1 -1
- package/dist/connectors/googleDocs.d.ts +103 -0
- package/dist/connectors/googleDocs.js +409 -0
- package/dist/connectors/googleDocs.js.map +1 -0
- package/dist/connectors/googleDrive.d.ts +12 -0
- package/dist/connectors/googleDrive.js +35 -8
- package/dist/connectors/googleDrive.js.map +1 -1
- package/dist/connectors/grafana.d.ts +133 -0
- package/dist/connectors/grafana.js +478 -0
- package/dist/connectors/grafana.js.map +1 -0
- package/dist/connectors/jira.d.ts +25 -0
- package/dist/connectors/jira.js +227 -1
- package/dist/connectors/jira.js.map +1 -1
- package/dist/connectors/linear.js +1 -1
- package/dist/connectors/linear.js.map +1 -1
- package/dist/connectors/mcpOAuth.js +78 -16
- package/dist/connectors/mcpOAuth.js.map +1 -1
- package/dist/connectors/monday.d.ts +217 -0
- package/dist/connectors/monday.js +655 -0
- package/dist/connectors/monday.js.map +1 -0
- package/dist/connectors/mongodb.d.ts +139 -0
- package/dist/connectors/mongodb.js +455 -0
- package/dist/connectors/mongodb.js.map +1 -0
- package/dist/connectors/oauthStateStore.d.ts +20 -0
- package/dist/connectors/oauthStateStore.js +94 -4
- package/dist/connectors/oauthStateStore.js.map +1 -1
- package/dist/connectors/obsidian.d.ts +83 -0
- package/dist/connectors/obsidian.js +441 -0
- package/dist/connectors/obsidian.js.map +1 -0
- package/dist/connectors/paystack.d.ts +159 -0
- package/dist/connectors/paystack.js +607 -0
- package/dist/connectors/paystack.js.map +1 -0
- package/dist/connectors/pipedrive.d.ts +189 -0
- package/dist/connectors/pipedrive.js +559 -0
- package/dist/connectors/pipedrive.js.map +1 -0
- package/dist/connectors/postgres.d.ts +127 -0
- package/dist/connectors/postgres.js +512 -0
- package/dist/connectors/postgres.js.map +1 -0
- package/dist/connectors/posthog.d.ts +119 -0
- package/dist/connectors/posthog.js +479 -0
- package/dist/connectors/posthog.js.map +1 -0
- package/dist/connectors/redis.d.ts +140 -0
- package/dist/connectors/redis.js +571 -0
- package/dist/connectors/redis.js.map +1 -0
- package/dist/connectors/resend.d.ts +131 -0
- package/dist/connectors/resend.js +529 -0
- package/dist/connectors/resend.js.map +1 -0
- package/dist/connectors/salesforce.d.ts +136 -0
- package/dist/connectors/salesforce.js +603 -0
- package/dist/connectors/salesforce.js.map +1 -0
- package/dist/connectors/secrets.d.ts +51 -0
- package/dist/connectors/secrets.js +102 -0
- package/dist/connectors/secrets.js.map +1 -0
- package/dist/connectors/sendgrid.d.ts +102 -0
- package/dist/connectors/sendgrid.js +423 -0
- package/dist/connectors/sendgrid.js.map +1 -0
- package/dist/connectors/shopify.d.ts +145 -0
- package/dist/connectors/shopify.js +502 -0
- package/dist/connectors/shopify.js.map +1 -0
- package/dist/connectors/slack.d.ts +1 -1
- package/dist/connectors/slack.js +61 -9
- package/dist/connectors/slack.js.map +1 -1
- package/dist/connectors/snowflake.d.ts +119 -0
- package/dist/connectors/snowflake.js +615 -0
- package/dist/connectors/snowflake.js.map +1 -0
- package/dist/connectors/supabase.d.ts +92 -0
- package/dist/connectors/supabase.js +630 -0
- package/dist/connectors/supabase.js.map +1 -0
- package/dist/connectors/todoist.d.ts +117 -0
- package/dist/connectors/todoist.js +485 -0
- package/dist/connectors/todoist.js.map +1 -0
- package/dist/connectors/tokenStorage.js +56 -14
- package/dist/connectors/tokenStorage.js.map +1 -1
- package/dist/connectors/twilio.d.ts +118 -0
- package/dist/connectors/twilio.js +475 -0
- package/dist/connectors/twilio.js.map +1 -0
- package/dist/connectors/vercel.d.ts +110 -0
- package/dist/connectors/vercel.js +479 -0
- package/dist/connectors/vercel.js.map +1 -0
- package/dist/connectors/webflow.d.ts +118 -0
- package/dist/connectors/webflow.js +393 -0
- package/dist/connectors/webflow.js.map +1 -0
- package/dist/connectors/woocommerce.d.ts +220 -0
- package/dist/connectors/woocommerce.js +464 -0
- package/dist/connectors/woocommerce.js.map +1 -0
- package/dist/crypto.js +7 -2
- package/dist/crypto.js.map +1 -1
- package/dist/decisionReplay.js +15 -6
- package/dist/decisionReplay.js.map +1 -1
- package/dist/decisionTraceLog.d.ts +28 -0
- package/dist/decisionTraceLog.js +156 -29
- package/dist/decisionTraceLog.js.map +1 -1
- package/dist/drivers/claude/api.js +5 -4
- package/dist/drivers/claude/api.js.map +1 -1
- package/dist/drivers/claude/envSanitizer.d.ts +0 -6
- package/dist/drivers/claude/envSanitizer.js +17 -2
- package/dist/drivers/claude/envSanitizer.js.map +1 -1
- package/dist/drivers/claude/streamParser.js +5 -4
- package/dist/drivers/claude/streamParser.js.map +1 -1
- package/dist/drivers/claude/subprocess.js +22 -3
- package/dist/drivers/claude/subprocess.js.map +1 -1
- package/dist/drivers/gemini/index.d.ts +22 -0
- package/dist/drivers/gemini/index.js +256 -129
- package/dist/drivers/gemini/index.js.map +1 -1
- package/dist/drivers/local/index.d.ts +17 -0
- package/dist/drivers/local/index.js +99 -0
- package/dist/drivers/local/index.js.map +1 -1
- package/dist/drivers/openai/index.js +30 -2
- package/dist/drivers/openai/index.js.map +1 -1
- package/dist/extensionClient.d.ts +37 -4
- package/dist/extensionClient.js +58 -16
- package/dist/extensionClient.js.map +1 -1
- package/dist/featureFlags.d.ts +91 -0
- package/dist/featureFlags.js +174 -3
- package/dist/featureFlags.js.map +1 -1
- package/dist/fileLock.js +21 -12
- package/dist/fileLock.js.map +1 -1
- package/dist/fileLockSync.d.ts +67 -0
- package/dist/fileLockSync.js +126 -0
- package/dist/fileLockSync.js.map +1 -0
- package/dist/fp/activityAnalytics.js +26 -12
- package/dist/fp/activityAnalytics.js.map +1 -1
- package/dist/fp/automationInterpreter.d.ts +15 -1
- package/dist/fp/automationInterpreter.js +217 -82
- package/dist/fp/automationInterpreter.js.map +1 -1
- package/dist/fp/automationProgram.d.ts +30 -0
- package/dist/fp/automationProgram.js.map +1 -1
- package/dist/fp/automationState.d.ts +24 -5
- package/dist/fp/automationState.js +56 -9
- package/dist/fp/automationState.js.map +1 -1
- package/dist/fp/automationUtils.js +1 -1
- package/dist/fp/automationUtils.js.map +1 -1
- package/dist/fp/commandDescription.js +7 -1
- package/dist/fp/commandDescription.js.map +1 -1
- package/dist/fp/extensionSnapshot.js +8 -2
- package/dist/fp/extensionSnapshot.js.map +1 -1
- package/dist/fp/interpreterContext.d.ts +66 -1
- package/dist/fp/interpreterContext.js +91 -1
- package/dist/fp/interpreterContext.js.map +1 -1
- package/dist/fp/policyParser.js +29 -1
- package/dist/fp/policyParser.js.map +1 -1
- package/dist/fsWatchWithFallback.d.ts +36 -0
- package/dist/fsWatchWithFallback.js +128 -0
- package/dist/fsWatchWithFallback.js.map +1 -0
- package/dist/haltPushDispatch.d.ts +33 -0
- package/dist/haltPushDispatch.js +103 -0
- package/dist/haltPushDispatch.js.map +1 -0
- package/dist/httpBodyValidation.d.ts +41 -0
- package/dist/httpBodyValidation.js +45 -0
- package/dist/httpBodyValidation.js.map +1 -0
- package/dist/inboxRoutes.d.ts +22 -0
- package/dist/inboxRoutes.js +61 -1
- package/dist/inboxRoutes.js.map +1 -1
- package/dist/index.js +1053 -76
- package/dist/index.js.map +1 -1
- package/dist/installGuard.js +6 -2
- package/dist/installGuard.js.map +1 -1
- package/dist/lockfile.js +31 -4
- package/dist/lockfile.js.map +1 -1
- package/dist/oauth.d.ts +9 -0
- package/dist/oauth.js +33 -0
- package/dist/oauth.js.map +1 -1
- package/dist/oauthRoutes.d.ts +1 -1
- package/dist/oauthRoutes.js +5 -3
- package/dist/oauthRoutes.js.map +1 -1
- package/dist/orchestrator/childBridgeRegistry.js +29 -11
- package/dist/orchestrator/childBridgeRegistry.js.map +1 -1
- package/dist/patchworkConfig.d.ts +44 -1
- package/dist/patchworkConfig.js +28 -3
- package/dist/patchworkConfig.js.map +1 -1
- package/dist/pluginLoader.js +10 -1
- package/dist/pluginLoader.js.map +1 -1
- package/dist/pluginWatcher.js +12 -14
- package/dist/pluginWatcher.js.map +1 -1
- package/dist/preToolUseHook.js +3 -2
- package/dist/preToolUseHook.js.map +1 -1
- package/dist/processTree.d.ts +34 -0
- package/dist/processTree.js +105 -0
- package/dist/processTree.js.map +1 -0
- package/dist/prompts.js +3 -3
- package/dist/prompts.js.map +1 -1
- package/dist/recipeOrchestration.d.ts +9 -0
- package/dist/recipeOrchestration.js +341 -24
- package/dist/recipeOrchestration.js.map +1 -1
- package/dist/recipeRoutes.d.ts +58 -2
- package/dist/recipeRoutes.js +744 -115
- package/dist/recipeRoutes.js.map +1 -1
- package/dist/recipes/RecipeOrchestrator.d.ts +2 -0
- package/dist/recipes/RecipeOrchestrator.js +6 -1
- package/dist/recipes/RecipeOrchestrator.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +25 -5
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/chainedRunner.d.ts +2 -0
- package/dist/recipes/chainedRunner.js +142 -5
- package/dist/recipes/chainedRunner.js.map +1 -1
- package/dist/recipes/connectorPreflight.d.ts +66 -0
- package/dist/recipes/connectorPreflight.js +169 -0
- package/dist/recipes/connectorPreflight.js.map +1 -0
- package/dist/recipes/dependencyGraph.js +13 -5
- package/dist/recipes/dependencyGraph.js.map +1 -1
- package/dist/recipes/githubInstallSource.d.ts +128 -0
- package/dist/recipes/githubInstallSource.js +206 -0
- package/dist/recipes/githubInstallSource.js.map +1 -0
- package/dist/recipes/haltCategory.d.ts +119 -0
- package/dist/recipes/haltCategory.js +188 -0
- package/dist/recipes/haltCategory.js.map +1 -0
- package/dist/recipes/idempotencyKey.d.ts +134 -0
- package/dist/recipes/idempotencyKey.js +322 -0
- package/dist/recipes/idempotencyKey.js.map +1 -0
- package/dist/recipes/installer.js +48 -2
- package/dist/recipes/installer.js.map +1 -1
- package/dist/recipes/judgeSummary.d.ts +50 -0
- package/dist/recipes/judgeSummary.js +47 -0
- package/dist/recipes/judgeSummary.js.map +1 -0
- package/dist/recipes/judgeVerdict.d.ts +48 -0
- package/dist/recipes/judgeVerdict.js +174 -0
- package/dist/recipes/judgeVerdict.js.map +1 -0
- package/dist/recipes/migrations/index.d.ts +9 -0
- package/dist/recipes/migrations/index.js +133 -0
- package/dist/recipes/migrations/index.js.map +1 -1
- package/dist/recipes/names.d.ts +20 -0
- package/dist/recipes/names.js +25 -0
- package/dist/recipes/names.js.map +1 -1
- package/dist/recipes/parser.js +88 -5
- package/dist/recipes/parser.js.map +1 -1
- package/dist/recipes/replayRun.js +1 -1
- package/dist/recipes/replayRun.js.map +1 -1
- package/dist/recipes/runBudget.d.ts +70 -0
- package/dist/recipes/runBudget.js +109 -0
- package/dist/recipes/runBudget.js.map +1 -0
- package/dist/recipes/scheduler.d.ts +30 -0
- package/dist/recipes/scheduler.js +69 -19
- package/dist/recipes/scheduler.js.map +1 -1
- package/dist/recipes/schema.d.ts +36 -0
- package/dist/recipes/schemaGenerator.js +103 -5
- package/dist/recipes/schemaGenerator.js.map +1 -1
- package/dist/recipes/stepObservation.js +9 -0
- package/dist/recipes/stepObservation.js.map +1 -1
- package/dist/recipes/toolRegistry.js +20 -3
- package/dist/recipes/toolRegistry.js.map +1 -1
- package/dist/recipes/tools/fanOut.d.ts +20 -0
- package/dist/recipes/tools/fanOut.js +199 -0
- package/dist/recipes/tools/fanOut.js.map +1 -0
- package/dist/recipes/tools/file.js +5 -2
- package/dist/recipes/tools/file.js.map +1 -1
- package/dist/recipes/tools/github.d.ts +1 -1
- package/dist/recipes/tools/github.js +75 -1
- package/dist/recipes/tools/github.js.map +1 -1
- package/dist/recipes/tools/gmail.js +27 -5
- package/dist/recipes/tools/gmail.js.map +1 -1
- package/dist/recipes/tools/googleDrive.js +64 -0
- package/dist/recipes/tools/googleDrive.js.map +1 -1
- package/dist/recipes/tools/http.d.ts +10 -0
- package/dist/recipes/tools/http.js +176 -0
- package/dist/recipes/tools/http.js.map +1 -0
- package/dist/recipes/tools/index.d.ts +2 -0
- package/dist/recipes/tools/index.js +2 -0
- package/dist/recipes/tools/index.js.map +1 -1
- package/dist/recipes/tools/slack.js +1 -1
- package/dist/recipes/validation.d.ts +17 -0
- package/dist/recipes/validation.js +29 -11
- package/dist/recipes/validation.js.map +1 -1
- package/dist/recipes/workspaceRoot.d.ts +37 -0
- package/dist/recipes/workspaceRoot.js +73 -0
- package/dist/recipes/workspaceRoot.js.map +1 -0
- package/dist/recipes/yamlPositions.d.ts +56 -0
- package/dist/recipes/yamlPositions.js +183 -0
- package/dist/recipes/yamlPositions.js.map +1 -0
- package/dist/recipes/yamlRunner.d.ts +182 -8
- package/dist/recipes/yamlRunner.js +877 -217
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/dist/recipesHttp.d.ts +19 -3
- package/dist/recipesHttp.js +67 -11
- package/dist/recipesHttp.js.map +1 -1
- package/dist/resources.js +21 -13
- package/dist/resources.js.map +1 -1
- package/dist/runLog.d.ts +58 -5
- package/dist/runLog.js +98 -21
- package/dist/runLog.js.map +1 -1
- package/dist/sanitizeParsedJson.d.ts +39 -0
- package/dist/sanitizeParsedJson.js +55 -0
- package/dist/sanitizeParsedJson.js.map +1 -0
- package/dist/schemas/recipe.v1.json +885 -196
- package/dist/server.d.ts +160 -3
- package/dist/server.js +1099 -122
- package/dist/server.js.map +1 -1
- package/dist/sessionCheckpoint.d.ts +8 -0
- package/dist/sessionCheckpoint.js +33 -3
- package/dist/sessionCheckpoint.js.map +1 -1
- package/dist/ssrfGuard.d.ts +16 -0
- package/dist/ssrfGuard.js +87 -4
- package/dist/ssrfGuard.js.map +1 -1
- package/dist/streamableHttp.d.ts +9 -4
- package/dist/streamableHttp.js +76 -20
- package/dist/streamableHttp.js.map +1 -1
- package/dist/telemetry.js +19 -12
- package/dist/telemetry.js.map +1 -1
- package/dist/testing/shadowRun.d.ts +52 -0
- package/dist/testing/shadowRun.js +71 -0
- package/dist/testing/shadowRun.js.map +1 -0
- package/dist/tools/batchLsp.d.ts +3 -0
- package/dist/tools/bridgeDoctor.d.ts +11 -0
- package/dist/tools/bridgeDoctor.js +48 -2
- package/dist/tools/bridgeDoctor.js.map +1 -1
- package/dist/tools/bridgeStatus.d.ts +0 -12
- package/dist/tools/bridgeStatus.js +2 -28
- package/dist/tools/bridgeStatus.js.map +1 -1
- package/dist/tools/cancelClaudeTask.d.ts +1 -0
- package/dist/tools/clipboard.d.ts +2 -0
- package/dist/tools/closeTabs.d.ts +1 -0
- package/dist/tools/codeLens.d.ts +1 -0
- package/dist/tools/createIssueFromAIComment.d.ts +1 -0
- package/dist/tools/ctxGetTaskContext.d.ts +9 -0
- package/dist/tools/ctxGetTaskContext.js +50 -2
- package/dist/tools/ctxGetTaskContext.js.map +1 -1
- package/dist/tools/ctxQueryTraces.js +32 -24
- package/dist/tools/ctxQueryTraces.js.map +1 -1
- package/dist/tools/ctxSaveTrace.d.ts +1 -0
- package/dist/tools/debug.d.ts +4 -0
- package/dist/tools/decorations.d.ts +2 -0
- package/dist/tools/detectUnusedCode.js +9 -7
- package/dist/tools/detectUnusedCode.js.map +1 -1
- package/dist/tools/documentLinks.d.ts +1 -0
- package/dist/tools/editText.d.ts +1 -0
- package/dist/tools/editText.js +2 -1
- package/dist/tools/editText.js.map +1 -1
- package/dist/tools/enrichCommit.d.ts +1 -0
- package/dist/tools/enrichStackTrace.js +11 -5
- package/dist/tools/enrichStackTrace.js.map +1 -1
- package/dist/tools/explainDiagnostic.d.ts +1 -0
- package/dist/tools/explainSymbol.d.ts +1 -0
- package/dist/tools/fileOperations.d.ts +3 -0
- package/dist/tools/fileOperations.js +2 -1
- package/dist/tools/fileOperations.js.map +1 -1
- package/dist/tools/fileWatcher.d.ts +2 -0
- package/dist/tools/fileWatcher.js +8 -2
- package/dist/tools/fileWatcher.js.map +1 -1
- package/dist/tools/findFiles.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.d.ts +1 -0
- package/dist/tools/fixAllLintErrors.js +10 -5
- package/dist/tools/fixAllLintErrors.js.map +1 -1
- package/dist/tools/foldingRanges.d.ts +1 -0
- package/dist/tools/formatDocument.d.ts +1 -0
- package/dist/tools/formatDocument.js +10 -5
- package/dist/tools/formatDocument.js.map +1 -1
- package/dist/tools/generateTests.d.ts +1 -0
- package/dist/tools/getAIComments.d.ts +1 -0
- package/dist/tools/getAnalyticsReport.js +5 -1
- package/dist/tools/getAnalyticsReport.js.map +1 -1
- package/dist/tools/getBufferContent.d.ts +1 -0
- package/dist/tools/getChangeImpact.d.ts +1 -0
- package/dist/tools/getChangeImpact.js +23 -14
- package/dist/tools/getChangeImpact.js.map +1 -1
- package/dist/tools/getClaudeTaskStatus.d.ts +1 -0
- package/dist/tools/getCodeCoverage.d.ts +1 -0
- package/dist/tools/getCodeCoverage.js +32 -14
- package/dist/tools/getCodeCoverage.js.map +1 -1
- package/dist/tools/getCommitsForIssue.d.ts +1 -0
- package/dist/tools/getDebugState.d.ts +1 -0
- package/dist/tools/getDiagnostics.js +32 -29
- package/dist/tools/getDiagnostics.js.map +1 -1
- package/dist/tools/getDiffFromHandoff.js +8 -2
- package/dist/tools/getDiffFromHandoff.js.map +1 -1
- package/dist/tools/getDocumentSymbols.d.ts +1 -0
- package/dist/tools/getGitHotspots.d.ts +1 -0
- package/dist/tools/getImportedSignatures.d.ts +1 -0
- package/dist/tools/getImportedSignatures.js +18 -14
- package/dist/tools/getImportedSignatures.js.map +1 -1
- package/dist/tools/getPRTemplate.d.ts +1 -0
- package/dist/tools/getProjectContext.js +23 -10
- package/dist/tools/getProjectContext.js.map +1 -1
- package/dist/tools/getSymbolHistory.d.ts +1 -0
- package/dist/tools/getToolCapabilities.d.ts +10 -5
- package/dist/tools/getToolCapabilities.js +79 -202
- package/dist/tools/getToolCapabilities.js.map +1 -1
- package/dist/tools/getTypeSignature.d.ts +1 -0
- package/dist/tools/getWorkspaceSettings.d.ts +1 -0
- package/dist/tools/gitWrite.d.ts +11 -0
- package/dist/tools/github/actions.d.ts +2 -0
- package/dist/tools/github/composite.d.ts +3 -0
- package/dist/tools/github/issues.d.ts +4 -0
- package/dist/tools/github/pr.d.ts +7 -0
- package/dist/tools/handoffNote.d.ts +1 -0
- package/dist/tools/handoffNote.js +2 -1
- package/dist/tools/handoffNote.js.map +1 -1
- package/dist/tools/headless/lspClient.js +3 -0
- package/dist/tools/headless/lspClient.js.map +1 -1
- package/dist/tools/hoverAtCursor.d.ts +1 -0
- package/dist/tools/httpClient.d.ts +2 -0
- package/dist/tools/httpClient.js +38 -71
- package/dist/tools/httpClient.js.map +1 -1
- package/dist/tools/inlayHints.d.ts +1 -0
- package/dist/tools/launchQuickTask.d.ts +1 -0
- package/dist/tools/listClaudeTasks.d.ts +1 -0
- package/dist/tools/listClaudeTasks.js +10 -8
- package/dist/tools/listClaudeTasks.js.map +1 -1
- package/dist/tools/listTerminals.d.ts +1 -0
- package/dist/tools/lsp.d.ts +15 -0
- package/dist/tools/lsp.js +17 -0
- package/dist/tools/lsp.js.map +1 -1
- package/dist/tools/navigateToSymbolByName.d.ts +1 -0
- package/dist/tools/openDiff.d.ts +1 -0
- package/dist/tools/openDiff.js +4 -1
- package/dist/tools/openDiff.js.map +1 -1
- package/dist/tools/openFile.d.ts +1 -0
- package/dist/tools/openFile.js +4 -1
- package/dist/tools/openFile.js.map +1 -1
- package/dist/tools/openInBrowser.js +6 -1
- package/dist/tools/openInBrowser.js.map +1 -1
- package/dist/tools/organizeImports.d.ts +1 -0
- package/dist/tools/organizeImports.js +5 -3
- package/dist/tools/organizeImports.js.map +1 -1
- package/dist/tools/performanceReport.js +5 -3
- package/dist/tools/performanceReport.js.map +1 -1
- package/dist/tools/planPersistence.d.ts +3 -0
- package/dist/tools/planPersistence.js +4 -1
- package/dist/tools/planPersistence.js.map +1 -1
- package/dist/tools/previewEdit.d.ts +1 -0
- package/dist/tools/previewEdit.js +15 -4
- package/dist/tools/previewEdit.js.map +1 -1
- package/dist/tools/recentTracesDigest.js +54 -11
- package/dist/tools/recentTracesDigest.js.map +1 -1
- package/dist/tools/refactorAnalyze.d.ts +1 -0
- package/dist/tools/refactorExtractFunction.js +4 -1
- package/dist/tools/refactorExtractFunction.js.map +1 -1
- package/dist/tools/refactorPreview.d.ts +1 -0
- package/dist/tools/refactorPreview.js +10 -2
- package/dist/tools/refactorPreview.js.map +1 -1
- package/dist/tools/replaceBlock.d.ts +1 -0
- package/dist/tools/replaceBlock.js +2 -1
- package/dist/tools/replaceBlock.js.map +1 -1
- package/dist/tools/resumeClaudeTask.d.ts +1 -0
- package/dist/tools/runClaudeTask.d.ts +1 -0
- package/dist/tools/runTests.js +15 -4
- package/dist/tools/runTests.js.map +1 -1
- package/dist/tools/screenshot.d.ts +1 -0
- package/dist/tools/screenshotAndAnnotate.js +6 -2
- package/dist/tools/screenshotAndAnnotate.js.map +1 -1
- package/dist/tools/searchAndReplace.d.ts +1 -0
- package/dist/tools/searchAndReplace.js +17 -7
- package/dist/tools/searchAndReplace.js.map +1 -1
- package/dist/tools/searchTools.js +20 -19
- package/dist/tools/searchTools.js.map +1 -1
- package/dist/tools/searchWorkspace.d.ts +1 -0
- package/dist/tools/selectionRanges.d.ts +1 -0
- package/dist/tools/semanticTokens.d.ts +1 -0
- package/dist/tools/signatureHelp.d.ts +1 -0
- package/dist/tools/spawnWorkspace.js +15 -7
- package/dist/tools/spawnWorkspace.js.map +1 -1
- package/dist/tools/terminal.d.ts +6 -0
- package/dist/tools/testRunners/pytest.js +6 -2
- package/dist/tools/testRunners/pytest.js.map +1 -1
- package/dist/tools/testRunners/vitestJest.js +3 -1
- package/dist/tools/testRunners/vitestJest.js.map +1 -1
- package/dist/tools/testTraceToSource.d.ts +1 -0
- package/dist/tools/transaction.d.ts +4 -0
- package/dist/tools/transaction.js +4 -1
- package/dist/tools/transaction.js.map +1 -1
- package/dist/tools/typeHierarchy.d.ts +1 -0
- package/dist/tools/utils.d.ts +18 -0
- package/dist/tools/utils.js +103 -18
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/vscodeCommands.d.ts +2 -0
- package/dist/tools/vscodeTasks.d.ts +2 -0
- package/dist/tools/workspaceSettings.d.ts +1 -0
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js +103 -52
- package/dist/transport.js.map +1 -1
- package/dist/winShim.d.ts +34 -0
- package/dist/winShim.js +94 -0
- package/dist/winShim.js.map +1 -0
- package/dist/wireHaltPushDispatch.d.ts +38 -0
- package/dist/wireHaltPushDispatch.js +71 -0
- package/dist/wireHaltPushDispatch.js.map +1 -0
- package/dist/writeFileAtomic.d.ts +23 -0
- package/dist/writeFileAtomic.js +121 -0
- package/dist/writeFileAtomic.js.map +1 -0
- package/package.json +23 -7
- package/scripts/postinstall.mjs +42 -2
- package/scripts/smoke/run-all.mjs +213 -0
- package/scripts/start-all.mjs +572 -0
- package/scripts/start-all.ps1 +209 -0
- package/scripts/start-all.sh +73 -17
- package/scripts/start-orchestrator.ps1 +158 -0
- package/scripts/start-remote.mjs +122 -0
- package/templates/automation-policies/recipe-authoring.json +1 -1
- package/templates/automation-policies/security-first.json +1 -1
- package/templates/automation-policies/strict-lint.json +1 -1
- package/templates/automation-policies/test-driven.json +1 -1
- package/templates/automation-policy.example.json +1 -1
- package/templates/co.patchwork-os.bridge.plist +2 -2
- package/templates/recipes/approval-queue-ui-test.yaml +1 -1
- package/templates/recipes/ctx-loop-test.yaml +1 -1
- package/templates/recipes/fix-errors-on-save.yaml +71 -0
- package/templates/recipes/morning-brief.yaml +5 -2
- package/templates/recipes/project-health-check.yaml +4 -1
- package/templates/recipes/sentry-to-linear.yaml +72 -38
- package/templates/recipes/webhook/apple-watch-health-log.yaml +145 -0
- package/templates/recipes/webhook/customer-escalation.yaml +8 -9
- package/templates/recipes/webhook/meeting-prep.yaml +11 -5
- package/dist/commands/marketplace.d.ts +0 -16
- package/dist/commands/marketplace.js +0 -32
- package/dist/commands/marketplace.js.map +0 -1
- package/dist/recipes/legacyRecipeCompat.d.ts +0 -10
- package/dist/recipes/legacyRecipeCompat.js +0 -131
- 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
|