@mastra/factory 0.24.0-alpha.0 → 0.24.0-alpha.10
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.md +106 -4
- package/dist/auth.d.ts +14 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +45 -9
- package/dist/auth.js.map +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +21 -1
- package/dist/boards/review.js.map +1 -1
- package/dist/boards/work-transition-policy.d.ts.map +1 -1
- package/dist/boards/work-transition-policy.js +12 -1
- package/dist/boards/work-transition-policy.js.map +1 -1
- package/dist/boards/work.d.ts +14 -0
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +70 -7
- package/dist/boards/work.js.map +1 -1
- package/dist/capabilities/version-control.d.ts +26 -0
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/factory.d.ts +8 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +354 -27
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +1 -1
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.js +1 -16
- package/dist/integrations/github/default-rules.js.map +1 -1
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +37 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/routes.d.ts +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +19 -11
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +26 -22
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox-release.d.ts +15 -3
- package/dist/integrations/github/sandbox-release.d.ts.map +1 -1
- package/dist/integrations/github/sandbox-release.js +20 -3
- package/dist/integrations/github/sandbox-release.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +2 -3
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +78 -7
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +1 -2
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/github/subscriptions.d.ts +7 -4
- package/dist/integrations/github/subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/subscriptions.js +5 -5
- package/dist/integrations/github/subscriptions.js.map +1 -1
- package/dist/integrations/github/webhook.d.ts +4 -2
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +59 -36
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/gitlab/api.d.ts +16 -1
- package/dist/integrations/gitlab/api.d.ts.map +1 -1
- package/dist/integrations/gitlab/api.js +9 -0
- package/dist/integrations/gitlab/api.js.map +1 -1
- package/dist/integrations/gitlab/routes.d.ts.map +1 -1
- package/dist/integrations/gitlab/routes.js +10 -6
- package/dist/integrations/gitlab/routes.js.map +1 -1
- package/dist/integrations/gitlab/rules.d.ts.map +1 -1
- package/dist/integrations/gitlab/rules.js +28 -21
- package/dist/integrations/gitlab/rules.js.map +1 -1
- package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/gitlab/session-subscriptions.js +1 -2
- package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
- package/dist/integrations/gitlab/subscriptions.d.ts +7 -4
- package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -1
- package/dist/integrations/gitlab/subscriptions.js +5 -5
- package/dist/integrations/gitlab/subscriptions.js.map +1 -1
- package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
- package/dist/integrations/gitlab/version-control.js +37 -3
- package/dist/integrations/gitlab/version-control.js.map +1 -1
- package/dist/integrations/incidentio/api.d.ts +9 -0
- package/dist/integrations/incidentio/api.d.ts.map +1 -1
- package/dist/integrations/incidentio/api.js +3 -0
- package/dist/integrations/incidentio/api.js.map +1 -1
- package/dist/integrations/incidentio/intake.d.ts +1 -1
- package/dist/integrations/incidentio/intake.d.ts.map +1 -1
- package/dist/integrations/incidentio/intake.js.map +1 -1
- package/dist/integrations/incidentio/integration.d.ts.map +1 -1
- package/dist/integrations/incidentio/integration.js +1 -0
- package/dist/integrations/incidentio/integration.js.map +1 -1
- package/dist/integrations/jira/api.d.ts +7 -0
- package/dist/integrations/jira/api.d.ts.map +1 -1
- package/dist/integrations/jira/api.js +4 -0
- package/dist/integrations/jira/api.js.map +1 -1
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/platform/api-client.d.ts +6 -0
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +6 -1
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/connect/routes.d.ts +1 -1
- package/dist/integrations/platform/connect/routes.d.ts.map +1 -1
- package/dist/integrations/platform/connect/routes.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +27 -1
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/incidentio/integration.d.ts +1 -1
- package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
- package/dist/integrations/platform/incidentio/integration.js +1 -0
- package/dist/integrations/platform/incidentio/integration.js.map +1 -1
- package/dist/integrations/platform/jira/integration.d.ts +1 -1
- package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
- package/dist/integrations/platform/jira/integration.js +1 -0
- package/dist/integrations/platform/jira/integration.js.map +1 -1
- package/dist/integrations/platform/linear/event-worker.d.ts +1 -1
- package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/linear/event-worker.js.map +1 -1
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +33 -6
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +16 -5
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +137 -64
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/integrations/subscription-session.d.ts +14 -0
- package/dist/integrations/subscription-session.d.ts.map +1 -1
- package/dist/integrations/subscription-session.js +30 -16
- package/dist/integrations/subscription-session.js.map +1 -1
- package/dist/mcp/disconnect.d.ts +10 -0
- package/dist/mcp/disconnect.d.ts.map +1 -0
- package/dist/mcp/disconnect.js +44 -0
- package/dist/mcp/disconnect.js.map +1 -0
- package/dist/mcp/routes.d.ts +46 -0
- package/dist/mcp/routes.d.ts.map +1 -0
- package/dist/mcp/routes.js +243 -0
- package/dist/mcp/routes.js.map +1 -0
- package/dist/provider-display-name.d.ts +2 -0
- package/dist/provider-display-name.d.ts.map +1 -0
- package/dist/provider-display-name.js +16 -0
- package/dist/provider-display-name.js.map +1 -0
- package/dist/review-verdict.d.ts +11 -0
- package/dist/review-verdict.d.ts.map +1 -1
- package/dist/review-verdict.js +16 -1
- package/dist/review-verdict.js.map +1 -1
- package/dist/routes/attention-activity.d.ts.map +1 -1
- package/dist/routes/attention-activity.js +5 -2
- package/dist/routes/attention-activity.js.map +1 -1
- package/dist/routes/attention-providers.d.ts +0 -1
- package/dist/routes/attention-providers.d.ts.map +1 -1
- package/dist/routes/attention-providers.js +5 -6
- package/dist/routes/attention-providers.js.map +1 -1
- package/dist/routes/config.d.ts +44 -5
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +181 -58
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/contracts.d.ts +4 -0
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/contracts.js +3 -1
- package/dist/routes/contracts.js.map +1 -1
- package/dist/routes/custom-provider-source.d.ts +8 -0
- package/dist/routes/custom-provider-source.d.ts.map +1 -1
- package/dist/routes/custom-provider-source.js +22 -1
- package/dist/routes/custom-provider-source.js.map +1 -1
- package/dist/routes/oauth.js +1 -1
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/projects.d.ts +1 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +1 -1
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/provider-credentials.d.ts +7 -2
- package/dist/routes/provider-credentials.d.ts.map +1 -1
- package/dist/routes/provider-credentials.js +30 -8
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/routes/skills.d.ts +2 -4
- package/dist/routes/skills.d.ts.map +1 -1
- package/dist/routes/skills.js +19 -65
- package/dist/routes/skills.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts +9 -1
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +131 -7
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/surface.d.ts +3 -2
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +7 -3
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +60 -120
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/binding-context.js +7 -7
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/dispatch-errors.d.ts.map +1 -1
- package/dist/rules/dispatch-errors.js +12 -0
- package/dist/rules/dispatch-errors.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +8 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +117 -12
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +2 -1
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +4 -0
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +36 -4
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +82 -3
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +17 -0
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +25 -0
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +2 -4
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +1 -4
- package/dist/rules/types.js.map +1 -1
- package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
- package/dist/sandbox/local-factory-sandbox.js +1 -0
- package/dist/sandbox/local-factory-sandbox.js.map +1 -1
- package/dist/sandbox/session-retirement.d.ts +27 -3
- package/dist/sandbox/session-retirement.d.ts.map +1 -1
- package/dist/sandbox/session-retirement.js +158 -20
- package/dist/sandbox/session-retirement.js.map +1 -1
- package/dist/secret-encryption.d.ts.map +1 -1
- package/dist/secret-encryption.js +65 -25
- package/dist/secret-encryption.js.map +1 -1
- package/dist/session/channel-reply-processor.d.ts +36 -0
- package/dist/session/channel-reply-processor.d.ts.map +1 -0
- package/dist/session/channel-reply-processor.js +46 -0
- package/dist/session/channel-reply-processor.js.map +1 -0
- package/dist/session/channel-session-link-processor.d.ts +39 -0
- package/dist/session/channel-session-link-processor.d.ts.map +1 -0
- package/dist/session/channel-session-link-processor.js +100 -0
- package/dist/session/channel-session-link-processor.js.map +1 -0
- package/dist/session/credential-policy.d.ts +1 -1
- package/dist/session/credential-policy.d.ts.map +1 -1
- package/dist/session/credential-policy.js +7 -6
- package/dist/session/credential-policy.js.map +1 -1
- package/dist/session/default-model-hydration.d.ts +1 -0
- package/dist/session/default-model-hydration.d.ts.map +1 -1
- package/dist/session/default-model-hydration.js +3 -2
- package/dist/session/default-model-hydration.js.map +1 -1
- package/dist/session/environment-state-processor.js +1 -1
- package/dist/session/environment-state-processor.js.map +1 -1
- package/dist/session/factory-session.d.ts +19 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +5 -2
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/filesystem-capture.d.ts +3 -3
- package/dist/session/filesystem-capture.d.ts.map +1 -1
- package/dist/session/filesystem-capture.js +16 -17
- package/dist/session/filesystem-capture.js.map +1 -1
- package/dist/session/first-exec-capture.d.ts +1 -1
- package/dist/session/first-exec-capture.d.ts.map +1 -1
- package/dist/session/first-exec-capture.js +1 -1
- package/dist/session/first-exec-capture.js.map +1 -1
- package/dist/session/first-message-capture.d.ts +1 -1
- package/dist/session/first-message-capture.d.ts.map +1 -1
- package/dist/session/first-message-capture.js +1 -1
- package/dist/session/first-message-capture.js.map +1 -1
- package/dist/session/live-sessions.d.ts +1 -1
- package/dist/session/live-sessions.js +1 -1
- package/dist/session/live-sessions.js.map +1 -1
- package/dist/session/memory-settings-hydration.d.ts +1 -1
- package/dist/session/memory-settings-hydration.d.ts.map +1 -1
- package/dist/session/memory-settings-hydration.js +1 -1
- package/dist/session/memory-settings-hydration.js.map +1 -1
- package/dist/session/reply-credential-processor.d.ts +15 -0
- package/dist/session/reply-credential-processor.d.ts.map +1 -0
- package/dist/session/reply-credential-processor.js +38 -0
- package/dist/session/reply-credential-processor.js.map +1 -0
- package/dist/session/resource-migration.d.ts +87 -0
- package/dist/session/resource-migration.d.ts.map +1 -0
- package/dist/session/resource-migration.js +163 -0
- package/dist/session/resource-migration.js.map +1 -0
- package/dist/session/run-audit.d.ts +24 -0
- package/dist/session/run-audit.d.ts.map +1 -1
- package/dist/session/run-audit.js +93 -7
- package/dist/session/run-audit.js.map +1 -1
- package/dist/session/session-threads.d.ts +49 -0
- package/dist/session/session-threads.d.ts.map +1 -0
- package/dist/session/session-threads.js +70 -0
- package/dist/session/session-threads.js.map +1 -0
- package/dist/session/source-control-tools.d.ts +19 -1
- package/dist/session/source-control-tools.d.ts.map +1 -1
- package/dist/session/source-control-tools.js +78 -11
- package/dist/session/source-control-tools.js.map +1 -1
- package/dist/session/thinking-default.d.ts.map +1 -1
- package/dist/session/thinking-default.js +3 -4
- package/dist/session/thinking-default.js.map +1 -1
- package/dist/session/thread-session-stamp.d.ts +21 -0
- package/dist/session/thread-session-stamp.d.ts.map +1 -0
- package/dist/session/thread-session-stamp.js +20 -0
- package/dist/session/thread-session-stamp.js.map +1 -0
- package/dist/session/thread-title-mirror.d.ts +1 -1
- package/dist/session/thread-title-mirror.d.ts.map +1 -1
- package/dist/session/thread-title-mirror.js +1 -1
- package/dist/session/thread-title-mirror.js.map +1 -1
- package/dist/session/work-item-repository.d.ts +1 -1
- package/dist/session/work-item-repository.d.ts.map +1 -1
- package/dist/session/work-item-repository.js.map +1 -1
- package/dist/skills/service.d.ts +6 -8
- package/dist/skills/service.d.ts.map +1 -1
- package/dist/skills/service.js +3 -3
- package/dist/skills/service.js.map +1 -1
- package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
- package/dist/storage/domains/audit/agent-audit.js +2 -2
- package/dist/storage/domains/audit/agent-audit.js.map +1 -1
- package/dist/storage/domains/audit/domain.js +1 -1
- package/dist/storage/domains/filesystem/base.d.ts +4 -0
- package/dist/storage/domains/filesystem/base.d.ts.map +1 -1
- package/dist/storage/domains/filesystem/base.js +5 -0
- package/dist/storage/domains/filesystem/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.d.ts +8 -0
- package/dist/storage/domains/integrations/base.d.ts.map +1 -1
- package/dist/storage/domains/integrations/base.js +7 -0
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/projects/base.d.ts +6 -0
- package/dist/storage/domains/projects/base.d.ts.map +1 -1
- package/dist/storage/domains/projects/base.js +12 -0
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +28 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +48 -0
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +8 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +13 -1
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +79 -7
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +172 -30
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/timing.d.ts +23 -0
- package/dist/timing.d.ts.map +1 -1
- package/dist/timing.js +85 -6
- package/dist/timing.js.map +1 -1
- package/dist/tools/annotations.d.ts +7 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +102 -0
- package/dist/tools/annotations.js.map +1 -0
- package/dist/tools/approval-tools.d.ts +15 -0
- package/dist/tools/approval-tools.d.ts.map +1 -0
- package/dist/tools/approval-tools.js +79 -0
- package/dist/tools/approval-tools.js.map +1 -0
- package/dist/tools/deliver.d.ts +71 -0
- package/dist/tools/deliver.d.ts.map +1 -0
- package/dist/tools/deliver.js +129 -0
- package/dist/tools/deliver.js.map +1 -0
- package/dist/tools/index.d.ts +30 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +36 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/messaging-tools.d.ts +28 -0
- package/dist/tools/messaging-tools.d.ts.map +1 -0
- package/dist/tools/messaging-tools.js +396 -0
- package/dist/tools/messaging-tools.js.map +1 -0
- package/dist/tools/permissions.d.ts +31 -0
- package/dist/tools/permissions.d.ts.map +1 -0
- package/dist/tools/permissions.js +48 -0
- package/dist/tools/permissions.js.map +1 -0
- package/dist/tools/read-tools.d.ts +30 -0
- package/dist/tools/read-tools.d.ts.map +1 -0
- package/dist/tools/read-tools.js +293 -0
- package/dist/tools/read-tools.js.map +1 -0
- package/dist/tools/reports.d.ts +174 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +380 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/run-watch.d.ts +46 -0
- package/dist/tools/run-watch.d.ts.map +1 -0
- package/dist/tools/run-watch.js +102 -0
- package/dist/tools/run-watch.js.map +1 -0
- package/dist/tools/scope.d.ts +54 -0
- package/dist/tools/scope.d.ts.map +1 -0
- package/dist/tools/scope.js +87 -0
- package/dist/tools/scope.js.map +1 -0
- package/dist/tools/session-tools.d.ts +43 -0
- package/dist/tools/session-tools.d.ts.map +1 -0
- package/dist/tools/session-tools.js +410 -0
- package/dist/tools/session-tools.js.map +1 -0
- package/dist/tools/sessions-state-processor.d.ts +41 -0
- package/dist/tools/sessions-state-processor.d.ts.map +1 -0
- package/dist/tools/sessions-state-processor.js +190 -0
- package/dist/tools/sessions-state-processor.js.map +1 -0
- package/dist/tools/side-sessions.d.ts +53 -0
- package/dist/tools/side-sessions.d.ts.map +1 -0
- package/dist/tools/side-sessions.js +83 -0
- package/dist/tools/side-sessions.js.map +1 -0
- package/dist/tools/work-item-tools.d.ts +28 -0
- package/dist/tools/work-item-tools.d.ts.map +1 -0
- package/dist/tools/work-item-tools.js +342 -0
- package/dist/tools/work-item-tools.js.map +1 -0
- package/dist/work-items/create-work-item.d.ts +55 -0
- package/dist/work-items/create-work-item.d.ts.map +1 -0
- package/dist/work-items/create-work-item.js +101 -0
- package/dist/work-items/create-work-item.js.map +1 -0
- package/dist/work-items/decision-actions.d.ts +42 -0
- package/dist/work-items/decision-actions.d.ts.map +1 -0
- package/dist/work-items/decision-actions.js +70 -0
- package/dist/work-items/decision-actions.js.map +1 -0
- package/dist/workspace.d.ts +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +15 -7
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
- package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
- package/factory-skills/factory-plan/SKILL.md +4 -4
- package/factory-skills/factory-rereview/SKILL.md +3 -1
- package/factory-skills/factory-review/SKILL.md +3 -1
- package/factory-skills/factory-self-review/SKILL.md +46 -0
- package/package.json +15 -15
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake.js","names":[],"sources":["../../../src/integrations/incidentio/intake.ts"],"sourcesContent":["import type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../capabilities/intake.js';\nimport {\n IncidentioApiClient,\n IncidentioApiError,\n type IncidentioActor,\n type IncidentioFollowUp,\n type IncidentioIncident,\n} from './api.js';\n\nexport const INCIDENTIO_INCIDENTS_SOURCE_ID = 'incidentio:incidents';\nexport const INCIDENTIO_FOLLOW_UPS_SOURCE_ID = 'incidentio:follow-ups';\n\nconst INCIDENT_PREFIX = 'incidentio:incident:';\nconst FOLLOW_UP_PREFIX = 'incidentio:follow-up:';\nconst SOURCE_IDS = [INCIDENTIO_INCIDENTS_SOURCE_ID, INCIDENTIO_FOLLOW_UPS_SOURCE_ID] as const;\n\ntype IncidentioItemReference = { type: 'incident' | 'follow-up'; id: string };\ntype IncidentioPageCursor = { sourceIndex: number; provider?: string };\ninterface ListedIncidentioIssue {\n issue: IntakeIssue;\n metadata: Record<string, unknown>;\n}\n\nexport function createIncidentioIntake(config: {\n api: IncidentioApiClient;\n connection: IntegrationConnection;\n}): Intake {\n const assertConnection = (connection: IntegrationConnection): void => {\n if (\n connection.type !== config.connection.type ||\n connection.type !== 'oauth' ||\n config.connection.type !== 'oauth' ||\n connection.accessToken !== config.connection.accessToken\n ) {\n throw new Error('incident.io Intake received a connection it did not create.');\n }\n };\n\n const listIssueEntries = async ({ sourceIds, labels, cursor }: ListIntakeIssuesInput) => {\n const selectedSources = SOURCE_IDS.filter(sourceId => sourceIds.includes(sourceId));\n const decodedCursor = decodePageCursor(cursor);\n if (decodedCursor.sourceIndex >= selectedSources.length) {\n return { entries: [] as ListedIncidentioIssue[], nextCursor: null };\n }\n\n const sourceId = selectedSources[decodedCursor.sourceIndex]!;\n let entries: ListedIncidentioIssue[];\n let providerCursor: string | null;\n if (sourceId === INCIDENTIO_INCIDENTS_SOURCE_ID) {\n const page = await config.api.listIncidents(decodedCursor.provider);\n entries = page.items.map(incident => {\n const issue = incidentToIntakeIssue(incident);\n return { issue, metadata: incidentIntakeMetadata(incident, issue) };\n });\n providerCursor = page.nextCursor;\n } else {\n const page = await config.api.listFollowUps(decodedCursor.provider);\n entries = page.items.map(followUp => {\n const issue = followUpToIntakeIssue(followUp);\n return { issue, metadata: followUpIntakeMetadata(followUp, issue) };\n });\n providerCursor = page.nextCursor;\n }\n entries = entries.filter(({ issue }) => isActive(issue.stateType) && matchesLabels(issue.labels, labels));\n const nextCursor = providerCursor\n ? encodePageCursor({ sourceIndex: decodedCursor.sourceIndex, provider: providerCursor })\n : decodedCursor.sourceIndex + 1 < selectedSources.length\n ? encodePageCursor({ sourceIndex: decodedCursor.sourceIndex + 1 })\n : null;\n return { entries, nextCursor };\n };\n\n const listIssues = async (input: ListIntakeIssuesInput) => {\n const page = await listIssueEntries(input);\n return { issues: page.entries.map(({ issue }) => issue), nextCursor: page.nextCursor };\n };\n\n const getIssue = async ({ connection, issueId }: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> => {\n assertConnection(connection);\n const reference = decodeItemReference(issueId);\n if (!reference) return null;\n try {\n if (reference.type === 'incident') {\n const incident = await config.api.getIncident(reference.id);\n return { ...incidentToIntakeIssue(incident), description: incident.summary ?? null, comments: [] };\n }\n const followUp = await config.api.getFollowUp(reference.id);\n return { ...followUpToIntakeIssue(followUp), description: followUp.description ?? null, comments: [] };\n } catch (error) {\n if (error instanceof IncidentioApiError && error.status === 404) return null;\n throw error;\n }\n };\n\n return {\n resolveIntakeDispatch: (input: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> => {\n if (input.externalSource.type !== 'issue') return Promise.resolve(null);\n const reference = decodeItemReference(input.externalSource.externalId);\n if (!reference) return Promise.resolve(null);\n return Promise.resolve({\n connection: config.connection,\n sourceId: reference.type === 'incident' ? INCIDENTIO_INCIDENTS_SOURCE_ID : INCIDENTIO_FOLLOW_UPS_SOURCE_ID,\n issueId: input.externalSource.externalId,\n });\n },\n listSources: async () => [\n { id: INCIDENTIO_INCIDENTS_SOURCE_ID, name: 'Incidents', type: 'incident' },\n { id: INCIDENTIO_FOLLOW_UPS_SOURCE_ID, name: 'Incident follow-ups', type: 'follow-up' },\n ],\n listItems: async (input: ListIntakeItemsInput): Promise<IntakeItemPage> => {\n const page = await listIssueEntries({\n connection: config.connection,\n sourceIds: input.sourceIds,\n ...(input.cursor ? { cursor: input.cursor } : {}),\n });\n return {\n items: page.entries.map(({ issue, metadata }) => ({\n source: { type: 'issue', externalId: issue.id, url: issue.url },\n sourceId: issue.id.startsWith(INCIDENT_PREFIX)\n ? INCIDENTIO_INCIDENTS_SOURCE_ID\n : INCIDENTIO_FOLLOW_UPS_SOURCE_ID,\n title: `${issue.identifier}: ${issue.title}`,\n status: issue.state ?? undefined,\n labels: issue.labels,\n assignee: issue.assignee,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata,\n })),\n nextCursor: page.nextCursor,\n };\n },\n listIssues: async input => {\n assertConnection(input.connection);\n return listIssues(input);\n },\n getIssue,\n createComment: async input => {\n assertConnection(input.connection);\n return null;\n },\n updateIssue: async (input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> => {\n assertConnection(input.connection);\n const reference = decodeItemReference(input.issueId);\n if (!reference || reference.type !== 'follow-up') return null;\n let followUp: IncidentioFollowUp;\n try {\n followUp = await config.api.getFollowUp(reference.id);\n } catch (error) {\n if (error instanceof IncidentioApiError && error.status === 404) return null;\n throw error;\n }\n const status = followUpStatusForTarget(input);\n if (!status) return null;\n if (followUp.status === status) return followUpToIntakeIssue(followUp);\n return followUpToIntakeIssue(await config.api.updateFollowUp(followUp, status));\n },\n };\n}\n\nfunction incidentToIntakeIssue(incident: IncidentioIncident): IntakeIssue {\n const labels = [incident.severity?.name, incident.incident_type?.name, incident.mode].filter(\n (label): label is string => Boolean(label),\n );\n return {\n id: `${INCIDENT_PREFIX}${incident.id}`,\n identifier: incident.reference,\n title: incident.name,\n url:\n incident.permalink ??\n incident.slack_channel_url ??\n incident.postmortem_document_url ??\n `https://app.incident.io/incidents/${encodeURIComponent(incident.id)}`,\n author: actorName(incident.creator),\n state: incident.incident_status.name,\n stateType: incidentStateType(incident.incident_status.category),\n priority: incident.severity?.name ?? null,\n assignee: null,\n source: 'Incident',\n labels,\n commentCount: 0,\n createdAt: incident.created_at,\n updatedAt: incident.updated_at,\n };\n}\n\nfunction followUpToIntakeIssue(followUp: IncidentioFollowUp): IntakeIssue {\n const assignees = [followUp.assignee?.name, followUp.assignee_team?.name].filter((assignee): assignee is string =>\n Boolean(assignee),\n );\n return {\n id: `${FOLLOW_UP_PREFIX}${followUp.id}`,\n identifier: followUp.external_issue_reference?.issue_name ?? followUp.id,\n title: followUp.title,\n url:\n followUp.external_issue_reference?.issue_permalink ??\n `https://app.incident.io/incidents/${encodeURIComponent(followUp.incident_id)}`,\n author: actorName(followUp.creator),\n state: followUp.status,\n stateType: followUpStateType(followUp.status),\n priority: followUp.priority?.name ?? null,\n assignee: assignees[0] ?? null,\n assignees,\n source: 'Follow-up',\n labels: followUp.labels,\n commentCount: 0,\n createdAt: followUp.created_at,\n updatedAt: followUp.updated_at,\n };\n}\n\nfunction intakeMetadata(issue: IntakeIssue): Record<string, unknown> {\n return {\n identifier: issue.identifier,\n autoStartCandidate: issue.stateType === 'unstarted' || issue.stateType === 'started',\n incidentioState: issue.state,\n incidentioStateType: issue.stateType,\n incidentioPriority: issue.priority,\n incidentioAssignee: issue.assignee,\n stateType: issue.stateType,\n priority: issue.priority,\n source: issue.source,\n assignee: issue.assignee,\n assignees: issue.assignees ?? [],\n creator: issue.author,\n author: issue.author,\n labels: issue.labels,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction incidentIntakeMetadata(incident: IncidentioIncident, issue: IntakeIssue): Record<string, unknown> {\n return {\n ...intakeMetadata(issue),\n incidentioItemType: 'incident',\n incidentioIncidentId: incident.id,\n ...(incident.summary ? { incidentioDescription: incident.summary } : {}),\n };\n}\n\nfunction followUpIntakeMetadata(followUp: IncidentioFollowUp, issue: IntakeIssue): Record<string, unknown> {\n return {\n ...intakeMetadata(issue),\n incidentioItemType: 'follow-up',\n incidentioIncidentId: followUp.incident_id,\n ...(followUp.description ? { incidentioDescription: followUp.description } : {}),\n };\n}\n\nfunction actorName(actor: IncidentioActor): string | null {\n return actor.user?.name ?? actor.api_key?.name ?? actor.workflow?.name ?? actor.alert?.title ?? null;\n}\n\nfunction incidentStateType(category: string): string | null {\n switch (category) {\n case 'triage':\n return 'unstarted';\n case 'declined':\n case 'canceled':\n return 'canceled';\n case 'merged':\n case 'closed':\n return 'completed';\n case 'live':\n case 'learning':\n case 'paused':\n return 'started';\n default:\n return null;\n }\n}\n\nfunction followUpStateType(status: IncidentioFollowUp['status']): string | null {\n switch (status) {\n case 'outstanding':\n return 'unstarted';\n case 'completed':\n return 'completed';\n case 'deleted':\n case 'not_doing':\n return 'canceled';\n default:\n return null;\n }\n}\n\nfunction followUpStatusForTarget(input: UpdateIntakeIssueInput): IncidentioFollowUp['status'] | null {\n if (input.state.kind === 'byType') {\n switch (input.state.stateType) {\n case 'unstarted':\n case 'started':\n return 'outstanding';\n case 'completed':\n return 'completed';\n case 'canceled':\n return 'not_doing';\n }\n }\n const status = input.state.name.toLowerCase().replaceAll('-', '_').replaceAll(' ', '_');\n return status === 'outstanding' || status === 'completed' || status === 'not_doing' ? status : null;\n}\n\nfunction isActive(stateType: string | null): boolean {\n return stateType !== 'completed' && stateType !== 'canceled';\n}\n\nfunction matchesLabels(issueLabels: string[], requestedLabels: string[] | undefined): boolean {\n if (!requestedLabels?.length) return true;\n const labels = new Set(issueLabels.map(label => label.toLowerCase()));\n return requestedLabels.every(label => labels.has(label.toLowerCase()));\n}\n\nfunction decodeItemReference(value: string): IncidentioItemReference | null {\n if (value.startsWith(INCIDENT_PREFIX)) return { type: 'incident', id: value.slice(INCIDENT_PREFIX.length) };\n if (value.startsWith(FOLLOW_UP_PREFIX)) return { type: 'follow-up', id: value.slice(FOLLOW_UP_PREFIX.length) };\n return null;\n}\n\nfunction encodePageCursor(cursor: IncidentioPageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): IncidentioPageCursor {\n if (!cursor) return { sourceIndex: 0 };\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Partial<IncidentioPageCursor>;\n if (!Number.isSafeInteger(parsed.sourceIndex) || (parsed.sourceIndex ?? -1) < 0) return { sourceIndex: 0 };\n return {\n sourceIndex: parsed.sourceIndex!,\n ...(typeof parsed.provider === 'string' && parsed.provider ? { provider: parsed.provider } : {}),\n };\n } catch {\n return { sourceIndex: 0 };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"intake.js","names":[],"sources":["../../../src/integrations/incidentio/intake.ts"],"sourcesContent":["import type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../capabilities/intake.js';\nimport { IncidentioApiError } from './api.js';\nimport type { IncidentioApiClient, IncidentioActor, IncidentioFollowUp, IncidentioIncident } from './api.js';\n\nexport const INCIDENTIO_INCIDENTS_SOURCE_ID = 'incidentio:incidents';\nexport const INCIDENTIO_FOLLOW_UPS_SOURCE_ID = 'incidentio:follow-ups';\n\nconst INCIDENT_PREFIX = 'incidentio:incident:';\nconst FOLLOW_UP_PREFIX = 'incidentio:follow-up:';\nconst SOURCE_IDS = [INCIDENTIO_INCIDENTS_SOURCE_ID, INCIDENTIO_FOLLOW_UPS_SOURCE_ID] as const;\n\ntype IncidentioItemReference = { type: 'incident' | 'follow-up'; id: string };\ntype IncidentioPageCursor = { sourceIndex: number; provider?: string };\ninterface ListedIncidentioIssue {\n issue: IntakeIssue;\n metadata: Record<string, unknown>;\n}\n\nexport function createIncidentioIntake(config: {\n api: IncidentioApiClient;\n connection: IntegrationConnection;\n}): Intake {\n const assertConnection = (connection: IntegrationConnection): void => {\n if (\n connection.type !== config.connection.type ||\n connection.type !== 'oauth' ||\n config.connection.type !== 'oauth' ||\n connection.accessToken !== config.connection.accessToken\n ) {\n throw new Error('incident.io Intake received a connection it did not create.');\n }\n };\n\n const listIssueEntries = async ({ sourceIds, labels, cursor }: ListIntakeIssuesInput) => {\n const selectedSources = SOURCE_IDS.filter(sourceId => sourceIds.includes(sourceId));\n const decodedCursor = decodePageCursor(cursor);\n if (decodedCursor.sourceIndex >= selectedSources.length) {\n return { entries: [] as ListedIncidentioIssue[], nextCursor: null };\n }\n\n const sourceId = selectedSources[decodedCursor.sourceIndex]!;\n let entries: ListedIncidentioIssue[];\n let providerCursor: string | null;\n if (sourceId === INCIDENTIO_INCIDENTS_SOURCE_ID) {\n const page = await config.api.listIncidents(decodedCursor.provider);\n entries = page.items.map(incident => {\n const issue = incidentToIntakeIssue(incident);\n return { issue, metadata: incidentIntakeMetadata(incident, issue) };\n });\n providerCursor = page.nextCursor;\n } else {\n const page = await config.api.listFollowUps(decodedCursor.provider);\n entries = page.items.map(followUp => {\n const issue = followUpToIntakeIssue(followUp);\n return { issue, metadata: followUpIntakeMetadata(followUp, issue) };\n });\n providerCursor = page.nextCursor;\n }\n entries = entries.filter(({ issue }) => isActive(issue.stateType) && matchesLabels(issue.labels, labels));\n const nextCursor = providerCursor\n ? encodePageCursor({ sourceIndex: decodedCursor.sourceIndex, provider: providerCursor })\n : decodedCursor.sourceIndex + 1 < selectedSources.length\n ? encodePageCursor({ sourceIndex: decodedCursor.sourceIndex + 1 })\n : null;\n return { entries, nextCursor };\n };\n\n const listIssues = async (input: ListIntakeIssuesInput) => {\n const page = await listIssueEntries(input);\n return { issues: page.entries.map(({ issue }) => issue), nextCursor: page.nextCursor };\n };\n\n const getIssue = async ({ connection, issueId }: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> => {\n assertConnection(connection);\n const reference = decodeItemReference(issueId);\n if (!reference) return null;\n try {\n if (reference.type === 'incident') {\n const incident = await config.api.getIncident(reference.id);\n return { ...incidentToIntakeIssue(incident), description: incident.summary ?? null, comments: [] };\n }\n const followUp = await config.api.getFollowUp(reference.id);\n return { ...followUpToIntakeIssue(followUp), description: followUp.description ?? null, comments: [] };\n } catch (error) {\n if (error instanceof IncidentioApiError && error.status === 404) return null;\n throw error;\n }\n };\n\n return {\n resolveIntakeDispatch: (input: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> => {\n if (input.externalSource.type !== 'issue') return Promise.resolve(null);\n const reference = decodeItemReference(input.externalSource.externalId);\n if (!reference) return Promise.resolve(null);\n return Promise.resolve({\n connection: config.connection,\n sourceId: reference.type === 'incident' ? INCIDENTIO_INCIDENTS_SOURCE_ID : INCIDENTIO_FOLLOW_UPS_SOURCE_ID,\n issueId: input.externalSource.externalId,\n });\n },\n listSources: async () => [\n { id: INCIDENTIO_INCIDENTS_SOURCE_ID, name: 'Incidents', type: 'incident' },\n { id: INCIDENTIO_FOLLOW_UPS_SOURCE_ID, name: 'Incident follow-ups', type: 'follow-up' },\n ],\n listItems: async (input: ListIntakeItemsInput): Promise<IntakeItemPage> => {\n const page = await listIssueEntries({\n connection: config.connection,\n sourceIds: input.sourceIds,\n ...(input.cursor ? { cursor: input.cursor } : {}),\n });\n return {\n items: page.entries.map(({ issue, metadata }) => ({\n source: { type: 'issue', externalId: issue.id, url: issue.url },\n sourceId: issue.id.startsWith(INCIDENT_PREFIX)\n ? INCIDENTIO_INCIDENTS_SOURCE_ID\n : INCIDENTIO_FOLLOW_UPS_SOURCE_ID,\n title: `${issue.identifier}: ${issue.title}`,\n status: issue.state ?? undefined,\n labels: issue.labels,\n assignee: issue.assignee,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n metadata,\n })),\n nextCursor: page.nextCursor,\n };\n },\n listIssues: async input => {\n assertConnection(input.connection);\n return listIssues(input);\n },\n getIssue,\n createComment: async input => {\n assertConnection(input.connection);\n return null;\n },\n updateIssue: async (input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> => {\n assertConnection(input.connection);\n const reference = decodeItemReference(input.issueId);\n if (!reference || reference.type !== 'follow-up') return null;\n let followUp: IncidentioFollowUp;\n try {\n followUp = await config.api.getFollowUp(reference.id);\n } catch (error) {\n if (error instanceof IncidentioApiError && error.status === 404) return null;\n throw error;\n }\n const status = followUpStatusForTarget(input);\n if (!status) return null;\n if (followUp.status === status) return followUpToIntakeIssue(followUp);\n return followUpToIntakeIssue(await config.api.updateFollowUp(followUp, status));\n },\n };\n}\n\nfunction incidentToIntakeIssue(incident: IncidentioIncident): IntakeIssue {\n const labels = [incident.severity?.name, incident.incident_type?.name, incident.mode].filter(\n (label): label is string => Boolean(label),\n );\n return {\n id: `${INCIDENT_PREFIX}${incident.id}`,\n identifier: incident.reference,\n title: incident.name,\n url:\n incident.permalink ??\n incident.slack_channel_url ??\n incident.postmortem_document_url ??\n `https://app.incident.io/incidents/${encodeURIComponent(incident.id)}`,\n author: actorName(incident.creator),\n state: incident.incident_status.name,\n stateType: incidentStateType(incident.incident_status.category),\n priority: incident.severity?.name ?? null,\n assignee: null,\n source: 'Incident',\n labels,\n commentCount: 0,\n createdAt: incident.created_at,\n updatedAt: incident.updated_at,\n };\n}\n\nfunction followUpToIntakeIssue(followUp: IncidentioFollowUp): IntakeIssue {\n const assignees = [followUp.assignee?.name, followUp.assignee_team?.name].filter((assignee): assignee is string =>\n Boolean(assignee),\n );\n return {\n id: `${FOLLOW_UP_PREFIX}${followUp.id}`,\n identifier: followUp.external_issue_reference?.issue_name ?? followUp.id,\n title: followUp.title,\n url:\n followUp.external_issue_reference?.issue_permalink ??\n `https://app.incident.io/incidents/${encodeURIComponent(followUp.incident_id)}`,\n author: actorName(followUp.creator),\n state: followUp.status,\n stateType: followUpStateType(followUp.status),\n priority: followUp.priority?.name ?? null,\n assignee: assignees[0] ?? null,\n assignees,\n source: 'Follow-up',\n labels: followUp.labels,\n commentCount: 0,\n createdAt: followUp.created_at,\n updatedAt: followUp.updated_at,\n };\n}\n\nfunction intakeMetadata(issue: IntakeIssue): Record<string, unknown> {\n return {\n identifier: issue.identifier,\n autoStartCandidate: issue.stateType === 'unstarted' || issue.stateType === 'started',\n incidentioState: issue.state,\n incidentioStateType: issue.stateType,\n incidentioPriority: issue.priority,\n incidentioAssignee: issue.assignee,\n stateType: issue.stateType,\n priority: issue.priority,\n source: issue.source,\n assignee: issue.assignee,\n assignees: issue.assignees ?? [],\n creator: issue.author,\n author: issue.author,\n labels: issue.labels,\n updatedAt: issue.updatedAt,\n };\n}\n\nfunction incidentIntakeMetadata(incident: IncidentioIncident, issue: IntakeIssue): Record<string, unknown> {\n return {\n ...intakeMetadata(issue),\n incidentioItemType: 'incident',\n incidentioIncidentId: incident.id,\n ...(incident.summary ? { incidentioDescription: incident.summary } : {}),\n };\n}\n\nfunction followUpIntakeMetadata(followUp: IncidentioFollowUp, issue: IntakeIssue): Record<string, unknown> {\n return {\n ...intakeMetadata(issue),\n incidentioItemType: 'follow-up',\n incidentioIncidentId: followUp.incident_id,\n ...(followUp.description ? { incidentioDescription: followUp.description } : {}),\n };\n}\n\nfunction actorName(actor: IncidentioActor): string | null {\n return actor.user?.name ?? actor.api_key?.name ?? actor.workflow?.name ?? actor.alert?.title ?? null;\n}\n\nfunction incidentStateType(category: string): string | null {\n switch (category) {\n case 'triage':\n return 'unstarted';\n case 'declined':\n case 'canceled':\n return 'canceled';\n case 'merged':\n case 'closed':\n return 'completed';\n case 'live':\n case 'learning':\n case 'paused':\n return 'started';\n default:\n return null;\n }\n}\n\nfunction followUpStateType(status: IncidentioFollowUp['status']): string | null {\n switch (status) {\n case 'outstanding':\n return 'unstarted';\n case 'completed':\n return 'completed';\n case 'deleted':\n case 'not_doing':\n return 'canceled';\n default:\n return null;\n }\n}\n\nfunction followUpStatusForTarget(input: UpdateIntakeIssueInput): IncidentioFollowUp['status'] | null {\n if (input.state.kind === 'byType') {\n switch (input.state.stateType) {\n case 'unstarted':\n case 'started':\n return 'outstanding';\n case 'completed':\n return 'completed';\n case 'canceled':\n return 'not_doing';\n }\n }\n const status = input.state.name.toLowerCase().replaceAll('-', '_').replaceAll(' ', '_');\n return status === 'outstanding' || status === 'completed' || status === 'not_doing' ? status : null;\n}\n\nfunction isActive(stateType: string | null): boolean {\n return stateType !== 'completed' && stateType !== 'canceled';\n}\n\nfunction matchesLabels(issueLabels: string[], requestedLabels: string[] | undefined): boolean {\n if (!requestedLabels?.length) return true;\n const labels = new Set(issueLabels.map(label => label.toLowerCase()));\n return requestedLabels.every(label => labels.has(label.toLowerCase()));\n}\n\nfunction decodeItemReference(value: string): IncidentioItemReference | null {\n if (value.startsWith(INCIDENT_PREFIX)) return { type: 'incident', id: value.slice(INCIDENT_PREFIX.length) };\n if (value.startsWith(FOLLOW_UP_PREFIX)) return { type: 'follow-up', id: value.slice(FOLLOW_UP_PREFIX.length) };\n return null;\n}\n\nfunction encodePageCursor(cursor: IncidentioPageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): IncidentioPageCursor {\n if (!cursor) return { sourceIndex: 0 };\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Partial<IncidentioPageCursor>;\n if (!Number.isSafeInteger(parsed.sourceIndex) || (parsed.sourceIndex ?? -1) < 0) return { sourceIndex: 0 };\n return {\n sourceIndex: parsed.sourceIndex!,\n ...(typeof parsed.provider === 'string' && parsed.provider ? { provider: parsed.provider } : {}),\n };\n } catch {\n return { sourceIndex: 0 };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/incidentio/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAOxF,MAAM,WAAW,2BAA2B;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC;AAKD,qBAAa,qBAAsB,YAAW,kBAAkB;;IAC9D,QAAQ,CAAC,EAAE,gBAAgB;IAC3B,QAAQ,CAAC,MAAM,gDAAC;gBAUJ,MAAM,GAAE,2BAAgC;
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/incidentio/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK3F,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAOxF,MAAM,WAAW,2BAA2B;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wEAAwE;IACxE,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC;AAKD,qBAAa,qBAAsB,YAAW,kBAAkB;;IAC9D,QAAQ,CAAC,EAAE,gBAAgB;IAC3B,QAAQ,CAAC,MAAM,gDAAC;gBAUJ,MAAM,GAAE,2BAAgC;IAsBpD,kFAAkF;IAClF,IAAI,KAAK,IAAI,oBAAoB,CAEhC;IAED;;;;OAIG;IACH,UAAU,CAAC,EACT,QAAQ,EACR,IAAI,EACJ,MAAM,GACP,EAAE;QACD,QAAQ,EAAE,sBAAsB,CAAC;QACjC,IAAI,EAAE,SAAS,CAAC;QAChB,MAAM,EAAE,aAAa,CAAC;KACvB,GAAG,IAAI;IAMR,oFAAoF;IACpF,IAAI,aAAa,IAAI,aAAa,CAKjC;IAED,+EAA+E;IAC/E,IAAI,QAAQ,IAAI,sBAAsB,CAKrC;IAED,sFAAsF;IACtF,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqB9D,oDAAoD;IACpD,WAAW,IAAI,IAAI;IAInB;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIrF,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,YAAY,EAAE;IAchD;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAU3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":["#endpointHost","#rules","#orgIdByResourceId","#projects","#auth","#intakeStorage"],"sources":["../../../src/integrations/incidentio/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { IntakeStorage } from '../../storage/domains/intake/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { IssueReconcileWorker } from '../issue-reconcile-worker.js';\nimport { buildIncidentioAgentTools } from './agent-tools.js';\nimport { IncidentioApiClient } from './api.js';\nimport { resolveIncidentioRules } from './default-rules.js';\nimport type { IncidentioEventRules, IncidentioRuleOverrides } from './default-rules.js';\nimport { createIncidentioIntake } from './intake.js';\nimport { attachIncidentioIssueReconciler } from './issue-reconciler.js';\nimport { incidentioReconciliationEnabled, incidentioReconciliationInterval } from './reconciliation-config.js';\nimport { buildIncidentioRoutes } from './routes.js';\nimport { attachIncidentioRules } from './rules.js';\n\nexport interface IncidentioIntegrationConfig {\n apiKey?: string;\n fetchImpl?: typeof fetch;\n baseUrl?: string;\n /** Per-event replacements for the default Factory incident.io rules. */\n rules?: IncidentioRuleOverrides;\n}\n\nconst DIRECT_CONNECTION_TOKEN = 'incidentio-direct-api-key';\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport class IncidentioIntegration implements FactoryIntegration {\n readonly id = 'incidentio';\n readonly intake;\n readonly #endpointHost: string;\n readonly #rules: IncidentioEventRules;\n\n /** Bound once by the factory via `initialize()` before any surface is used. */\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n #intakeStorage: IntakeStorage | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n constructor(config: IncidentioIntegrationConfig = {}) {\n this.#rules = resolveIncidentioRules(config.rules);\n const apiKey = config.apiKey?.trim() || process.env.INCIDENT_IO_API_KEY?.trim();\n if (!apiKey) {\n throw new Error('IncidentioIntegration: missing required INCIDENT_IO_API_KEY.');\n }\n const baseUrl = config.baseUrl ?? 'https://api.incident.io';\n this.#endpointHost = new URL(baseUrl).host;\n const api = new IncidentioApiClient({\n baseUrl,\n accessToken: apiKey,\n ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),\n });\n this.intake = createIncidentioIntake({\n api,\n connection: { type: 'oauth', accessToken: DIRECT_CONNECTION_TOKEN },\n });\n }\n\n /** Event rules driving automatic follow-up materialization and close handling. */\n get rules(): IncidentioEventRules {\n return this.#rules;\n }\n\n /**\n * Bind the projects domain and the host auth seam. incident.io has no\n * per-org connection rows here — credentials are deployment-global\n * constructor config.\n */\n initialize({\n projects,\n auth,\n intake,\n }: {\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n intake: IntakeStorage;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#intakeStorage = intake;\n }\n\n /** Cross-integration intake selection/binding domain — agent-tool authorization. */\n get intakeStorage(): IntakeStorage {\n if (!this.#intakeStorage) {\n throw new Error('IncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#intakeStorage;\n }\n\n /** Factory projects domain — maps a session's resourceId to its owning org. */\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error('IncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#projects;\n }\n\n /** Whether the host runs with web auth enabled; every surface is inert without it. */\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n /**\n * Map a session's resourceId (the factory project id) to its owning org.\n * Same caching semantics as the Jira integration: definitive misses are\n * cached, transient database failures are not.\n */\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n let orgId: string | null;\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n orgId = project?.orgId ?? null;\n } catch {\n // Transient database failure: skip the tools for this request but don't\n // cache the miss, so the next request retries the lookup.\n return null;\n }\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n }\n\n /** Test hook: clear the org cache between specs. */\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n /**\n * Org-scoped agent tools: the read-only follow-up detail tool for sessions\n * whose resource is a factory project, matching the Linear/Jira surface.\n */\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildIncidentioAgentTools({ requestContext: args.requestContext, incidentio: this });\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!incidentioReconciliationEnabled()) return [];\n const reconcile = attachIncidentioIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = incidentioReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n /**\n * The integration's HTTP surface: `/web/incidentio/*` Mastra `apiRoutes`\n * (status + follow-up listing/detail for Intake). Handlers operate on this\n * instance; a board-scoped listing also feeds the incident.io event rules.\n */\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildIncidentioRoutes({\n incidentio: this,\n mode: 'api-key',\n auth: ctx.auth,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachIncidentioRules(this, ctx),\n });\n }\n\n diagnostics(): Record<string, unknown> {\n return { mode: 'api-key', endpointHost: this.#endpointHost };\n }\n}\n"],"mappings":";;;;;;;;;;AA2BA,MAAM,0BAA0B;AAChC,MAAM,eAAe;AAErB,IAAa,wBAAb,MAAiE;CAC/D,KAAc;CACd;CACA;CACA;;CAGA;CACA;CACA;CACA,qCAA8B,IAAI,IAA2B;CAE7D,YAAY,SAAsC,CAAC,GAAG;EACpD,KAAKC,SAAS,uBAAuB,OAAO,KAAK;EACjD,MAAM,SAAS,OAAO,QAAQ,KAAK,KAAK,QAAQ,IAAI,qBAAqB,KAAK;EAC9E,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,8DAA8D;EAEhF,MAAM,UAAU,OAAO,WAAW;EAClC,KAAKD,gBAAgB,IAAI,IAAI,OAAO,CAAC,CAAC;EACtC,MAAM,MAAM,IAAI,oBAAoB;GAClC;GACA,aAAa;
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#endpointHost","#rules","#orgIdByResourceId","#projects","#auth","#intakeStorage"],"sources":["../../../src/integrations/incidentio/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { RouteAuth } from '../../routes/route.js';\nimport type { IntakeStorage } from '../../storage/domains/intake/base.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../base.js';\nimport { IssueReconcileWorker } from '../issue-reconcile-worker.js';\nimport { buildIncidentioAgentTools } from './agent-tools.js';\nimport { IncidentioApiClient } from './api.js';\nimport { resolveIncidentioRules } from './default-rules.js';\nimport type { IncidentioEventRules, IncidentioRuleOverrides } from './default-rules.js';\nimport { createIncidentioIntake } from './intake.js';\nimport { attachIncidentioIssueReconciler } from './issue-reconciler.js';\nimport { incidentioReconciliationEnabled, incidentioReconciliationInterval } from './reconciliation-config.js';\nimport { buildIncidentioRoutes } from './routes.js';\nimport { attachIncidentioRules } from './rules.js';\n\nexport interface IncidentioIntegrationConfig {\n apiKey?: string;\n fetchImpl?: typeof fetch;\n baseUrl?: string;\n /** Per-event replacements for the default Factory incident.io rules. */\n rules?: IncidentioRuleOverrides;\n}\n\nconst DIRECT_CONNECTION_TOKEN = 'incidentio-direct-api-key';\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\nexport class IncidentioIntegration implements FactoryIntegration {\n readonly id = 'incidentio';\n readonly intake;\n readonly #endpointHost: string;\n readonly #rules: IncidentioEventRules;\n\n /** Bound once by the factory via `initialize()` before any surface is used. */\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n #intakeStorage: IntakeStorage | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n constructor(config: IncidentioIntegrationConfig = {}) {\n this.#rules = resolveIncidentioRules(config.rules);\n const apiKey = config.apiKey?.trim() || process.env.INCIDENT_IO_API_KEY?.trim();\n if (!apiKey) {\n throw new Error('IncidentioIntegration: missing required INCIDENT_IO_API_KEY.');\n }\n const baseUrl = config.baseUrl ?? 'https://api.incident.io';\n this.#endpointHost = new URL(baseUrl).host;\n const api = new IncidentioApiClient({\n baseUrl,\n accessToken: apiKey,\n // Direct incident.io access: never leak the Mastra project id header\n // to incident.io itself.\n projectId: null,\n ...(config.fetchImpl ? { fetchImpl: config.fetchImpl } : {}),\n });\n this.intake = createIncidentioIntake({\n api,\n connection: { type: 'oauth', accessToken: DIRECT_CONNECTION_TOKEN },\n });\n }\n\n /** Event rules driving automatic follow-up materialization and close handling. */\n get rules(): IncidentioEventRules {\n return this.#rules;\n }\n\n /**\n * Bind the projects domain and the host auth seam. incident.io has no\n * per-org connection rows here — credentials are deployment-global\n * constructor config.\n */\n initialize({\n projects,\n auth,\n intake,\n }: {\n projects: FactoryProjectsStorage;\n auth: RouteAuth;\n intake: IntakeStorage;\n }): void {\n this.#projects = projects;\n this.#auth = auth;\n this.#intakeStorage = intake;\n }\n\n /** Cross-integration intake selection/binding domain — agent-tool authorization. */\n get intakeStorage(): IntakeStorage {\n if (!this.#intakeStorage) {\n throw new Error('IncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#intakeStorage;\n }\n\n /** Factory projects domain — maps a session's resourceId to its owning org. */\n get projects(): FactoryProjectsStorage {\n if (!this.#projects) {\n throw new Error('IncidentioIntegration is not initialized — the factory binds storage during prepare().');\n }\n return this.#projects;\n }\n\n /** Whether the host runs with web auth enabled; every surface is inert without it. */\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n /**\n * Map a session's resourceId (the factory project id) to its owning org.\n * Same caching semantics as the Jira integration: definitive misses are\n * cached, transient database failures are not.\n */\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n let orgId: string | null;\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n orgId = project?.orgId ?? null;\n } catch {\n // Transient database failure: skip the tools for this request but don't\n // cache the miss, so the next request retries the lookup.\n return null;\n }\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n }\n\n /** Test hook: clear the org cache between specs. */\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n }\n\n /**\n * Org-scoped agent tools: the read-only follow-up detail tool for sessions\n * whose resource is a factory project, matching the Linear/Jira surface.\n */\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildIncidentioAgentTools({ requestContext: args.requestContext, incidentio: this });\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!incidentioReconciliationEnabled()) return [];\n const reconcile = attachIncidentioIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = incidentioReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n /**\n * The integration's HTTP surface: `/web/incidentio/*` Mastra `apiRoutes`\n * (status + follow-up listing/detail for Intake). Handlers operate on this\n * instance; a board-scoped listing also feeds the incident.io event rules.\n */\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildIncidentioRoutes({\n incidentio: this,\n mode: 'api-key',\n auth: ctx.auth,\n intake: ctx.storage.intake,\n ingestFactoryIssues: attachIncidentioRules(this, ctx),\n });\n }\n\n diagnostics(): Record<string, unknown> {\n return { mode: 'api-key', endpointHost: this.#endpointHost };\n }\n}\n"],"mappings":";;;;;;;;;;AA2BA,MAAM,0BAA0B;AAChC,MAAM,eAAe;AAErB,IAAa,wBAAb,MAAiE;CAC/D,KAAc;CACd;CACA;CACA;;CAGA;CACA;CACA;CACA,qCAA8B,IAAI,IAA2B;CAE7D,YAAY,SAAsC,CAAC,GAAG;EACpD,KAAKC,SAAS,uBAAuB,OAAO,KAAK;EACjD,MAAM,SAAS,OAAO,QAAQ,KAAK,KAAK,QAAQ,IAAI,qBAAqB,KAAK;EAC9E,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,8DAA8D;EAEhF,MAAM,UAAU,OAAO,WAAW;EAClC,KAAKD,gBAAgB,IAAI,IAAI,OAAO,CAAC,CAAC;EACtC,MAAM,MAAM,IAAI,oBAAoB;GAClC;GACA,aAAa;GAGb,WAAW;GACX,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;EAC5D,CAAC;EACD,KAAK,SAAS,uBAAuB;GACnC;GACA,YAAY;IAAE,MAAM;IAAS,aAAa;GAAwB;EACpE,CAAC;CACH;;CAGA,IAAI,QAA8B;EAChC,OAAO,KAAKC;CACd;;;;;;CAOA,WAAW,EACT,UACA,MACA,UAKO;EACP,KAAKE,YAAY;EACjB,KAAKC,QAAQ;EACb,KAAKC,iBAAiB;CACxB;;CAGA,IAAI,gBAA+B;EACjC,IAAI,CAAC,KAAKA,gBACR,MAAM,IAAI,MAAM,wFAAwF;EAE1G,OAAO,KAAKA;CACd;;CAGA,IAAI,WAAmC;EACrC,IAAI,CAAC,KAAKF,WACR,MAAM,IAAI,MAAM,wFAAwF;EAE1G,OAAO,KAAKA;CACd;;CAGA,IAAI,cAAuB;EACzB,OAAO,KAAKC,OAAO,QAAQ,KAAK;CAClC;;;;;;CAOA,MAAM,aAAa,YAA4C;EAC7D,MAAM,SAAS,KAAKF,mBAAmB,IAAI,UAAU;EACrD,IAAI,WAAW,KAAA,GAAW,OAAO;EACjC,IAAI,CAAC,aAAa,KAAK,UAAU,GAAG;GAClC,KAAKA,mBAAmB,IAAI,YAAY,IAAI;GAC5C,OAAO;EACT;EACA,IAAI;EACJ,IAAI;GACF,MAAM,KAAK,SAAS,YAAY;GAEhC,SAAQ,MADc,KAAK,SAAS,QAAQ,EAAE,IAAI,WAAW,CAAC,EAAA,EAC7C,SAAS;EAC5B,QAAQ;GAGN,OAAO;EACT;EACA,KAAKA,mBAAmB,IAAI,YAAY,KAAK;EAC7C,OAAO;CACT;;CAGA,cAAoB;EAClB,KAAKA,mBAAmB,MAAM;CAChC;;;;;CAMA,MAAM,WAAW,MAAqE;EACpF,OAAO,0BAA0B;GAAE,gBAAgB,KAAK;GAAgB,YAAY;EAAK,CAAC;CAC5F;CAEA,QAAQ,KAAyC;EAC/C,IAAI,CAAC,gCAAgC,GAAG,OAAO,CAAC;EAChD,MAAM,YAAY,gCAAgC,MAAM,GAAG;EAC3D,IAAI,CAAC,WAAW,OAAO,CAAC;EACxB,MAAM,aAAa,iCAAiC;EACpD,OAAO,CACL,IAAI,qBAAqB;GACvB,eAAe,KAAK;GACpB;GACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;EACrC,CAAC,CACH;CACF;;;;;;CAOA,OAAO,KAAqC;EAC1C,OAAO,sBAAsB;GAC3B,YAAY;GACZ,MAAM;GACN,MAAM,IAAI;GACV,QAAQ,IAAI,QAAQ;GACpB,qBAAqB,sBAAsB,MAAM,GAAG;EACtD,CAAC;CACH;CAEA,cAAuC;EACrC,OAAO;GAAE,MAAM;GAAW,cAAc,KAAKF;EAAc;CAC7D;AACF"}
|
|
@@ -37,6 +37,13 @@ export type JiraApiClientConfig = JiraApiClientBaseConfig & ({
|
|
|
37
37
|
} | {
|
|
38
38
|
/** Bearer token used when the base URL is a Mastra Platform connection proxy. */
|
|
39
39
|
accessToken: string;
|
|
40
|
+
/**
|
|
41
|
+
* Mastra project id sent as `x-mastra-project-id` so the platform can
|
|
42
|
+
* attribute proxied Jira activity to the project. Falls back to the
|
|
43
|
+
* `MASTRA_PROJECT_ID` environment variable. The platform strips the
|
|
44
|
+
* header before forwarding the request to Atlassian.
|
|
45
|
+
*/
|
|
46
|
+
projectId?: string;
|
|
40
47
|
email?: never;
|
|
41
48
|
apiToken?: never;
|
|
42
49
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/integrations/jira/api.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/integrations/jira/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,6 +41,7 @@ var JiraApiError = class extends Error {
|
|
|
41
41
|
var JiraApiClient = class {
|
|
42
42
|
#baseUrl;
|
|
43
43
|
#authHeader;
|
|
44
|
+
#projectId;
|
|
44
45
|
#fetch;
|
|
45
46
|
constructor(config) {
|
|
46
47
|
if (!config.baseUrl) throw new Error("JiraApiClient is missing required config: baseUrl.");
|
|
@@ -54,11 +55,13 @@ var JiraApiClient = class {
|
|
|
54
55
|
if (config.accessToken !== void 0) {
|
|
55
56
|
if (!config.accessToken) throw new Error("JiraApiClient is missing required config: accessToken.");
|
|
56
57
|
this.#authHeader = `Bearer ${config.accessToken}`;
|
|
58
|
+
this.#projectId = (config.projectId ?? process.env.MASTRA_PROJECT_ID)?.trim() || null;
|
|
57
59
|
} else {
|
|
58
60
|
const missing = ["email", "apiToken"].filter((key) => !config[key]);
|
|
59
61
|
if (missing.length > 0) throw new Error(`JiraApiClient is missing required config: ${missing.join(", ")}.`);
|
|
60
62
|
if (parsed.protocol === "http:" && !LOOPBACK_HOSTS.has(parsed.hostname)) throw new Error("JiraApiClient baseUrl must use https when Basic credentials are configured.");
|
|
61
63
|
this.#authHeader = `Basic ${Buffer.from(`${config.email}:${config.apiToken}`).toString("base64")}`;
|
|
64
|
+
this.#projectId = null;
|
|
62
65
|
}
|
|
63
66
|
this.#baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
64
67
|
this.#fetch = config.fetchImpl ?? globalThis.fetch;
|
|
@@ -77,6 +80,7 @@ var JiraApiClient = class {
|
|
|
77
80
|
headers: {
|
|
78
81
|
authorization: this.#authHeader,
|
|
79
82
|
accept: "application/json",
|
|
83
|
+
...this.#projectId ? { "x-mastra-project-id": this.#projectId } : {},
|
|
80
84
|
...hasBody ? { "content-type": "application/json" } : {}
|
|
81
85
|
},
|
|
82
86
|
...hasBody ? { body: JSON.stringify(options.body) } : {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","names":["#baseUrl","#authHeader","#fetch","#request"],"sources":["../../../src/integrations/jira/api.ts"],"sourcesContent":["/**\n * Typed Jira Cloud REST client (API v3).\n *\n * Shared by the direct and Platform-managed Jira integrations. Direct Jira\n * uses Basic auth against an Atlassian site; Platform Jira points the same\n * client at a connection proxy with a bearer token. Both modes use the same\n * request shapes, timeout, and error normalization.\n *\n * Endpoint notes (Jira Cloud, 2026):\n * - Issue search is `POST /rest/api/3/search/jql` — the legacy\n * `GET /rest/api/3/search` endpoint has been removed. The new endpoint\n * returns only ids unless `fields` is requested explicitly, and pages with\n * an opaque `nextPageToken` cursor (no `startAt`/`total`).\n * - Project listing is `GET /rest/api/3/project/search` with classic\n * `startAt`/`isLast` paging.\n * - Descriptions and comment bodies are ADF documents (see `adf.ts`).\n */\n\nimport type { AdfNode } from './adf.js';\nimport { textToAdf } from './adf.js';\n\nconst JIRA_TIMEOUT_MS = 15_000;\n/** Hosts allowed to receive Basic credentials over plain http (local development mocks). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n/** Issue page size — Linear parity (`LINEAR_ISSUES_PAGE_SIZE`). */\nexport const JIRA_ISSUES_PAGE_SIZE = 30;\n/** Comment page size — Linear parity (`LINEAR_COMMENTS_PAGE_SIZE`). */\nexport const JIRA_COMMENTS_PAGE_SIZE = 50;\nexport const JIRA_PROJECTS_PAGE_SIZE = 50;\n\n/** Fields requested from issue reads — `search/jql` returns ids only without an explicit list. */\nexport const JIRA_ISSUE_FIELDS = [\n 'summary',\n 'description',\n 'status',\n 'assignee',\n 'reporter',\n 'labels',\n 'priority',\n 'issuetype',\n 'project',\n 'created',\n 'updated',\n] as const;\n\ninterface JiraApiClientBaseConfig {\n /** Jira API base URL, either the Atlassian site or a Platform connection proxy. */\n baseUrl: string;\n fetchImpl?: typeof fetch;\n}\n\nexport type JiraApiClientConfig = JiraApiClientBaseConfig &\n (\n | {\n /** Atlassian account email the API token belongs to. */\n email: string;\n /** API token from id.atlassian.com → Security → API tokens. */\n apiToken: string;\n accessToken?: never;\n }\n | {\n /** Bearer token used when the base URL is a Mastra Platform connection proxy. */\n accessToken: string;\n email?: never;\n apiToken?: never;\n }\n );\n\n/** Stable error code the routes/tools surface to the SPA and agents. */\nexport type JiraApiErrorCode = 'jira_auth_failed' | 'jira_request_failed';\n\n/** Normalized Jira API failure carrying the HTTP status and Jira's error detail. */\nexport class JiraApiError extends Error {\n readonly status: number | null;\n readonly code: JiraApiErrorCode;\n\n constructor(message: string, status: number | null) {\n super(message);\n this.name = 'JiraApiError';\n this.status = status;\n this.code = status === 401 || status === 403 ? 'jira_auth_failed' : 'jira_request_failed';\n }\n}\n\nexport interface JiraStatusCategory {\n /** `new` | `indeterminate` | `done` — Jira's fixed status families. */\n key: string;\n name?: string;\n}\n\nexport interface JiraStatus {\n name: string;\n statusCategory?: JiraStatusCategory;\n}\n\nexport interface JiraProject {\n id: string;\n key: string;\n name: string;\n}\n\nexport interface JiraProjectSearchPage {\n values: JiraProject[];\n startAt: number;\n isLast: boolean;\n}\n\nexport interface JiraServerInfo {\n baseUrl: string;\n serverTitle?: string;\n}\n\nexport interface JiraUser {\n displayName: string;\n}\n\nexport interface JiraComment {\n id: string;\n author?: JiraUser | null;\n body?: AdfNode | null;\n created: string;\n}\n\nexport interface JiraCommentPage {\n comments: JiraComment[];\n startAt: number;\n maxResults: number;\n total: number;\n}\n\nexport interface JiraIssueFields {\n summary?: string;\n description?: AdfNode | null;\n status?: JiraStatus;\n assignee?: JiraUser | null;\n reporter?: JiraUser | null;\n labels?: string[];\n priority?: { name: string } | null;\n issuetype?: { name: string };\n project?: { id: string; key: string; name?: string };\n created?: string;\n updated?: string;\n}\n\nexport interface JiraIssue {\n id: string;\n key: string;\n fields: JiraIssueFields;\n}\n\nexport interface JiraSearchPage {\n issues: JiraIssue[];\n /** Opaque cursor for the next page; absent on the last page. */\n nextPageToken?: string | null;\n}\n\nexport interface JiraTransition {\n id: string;\n name: string;\n /** Target status the transition moves the issue to. */\n to?: JiraStatus;\n}\n\nexport class JiraApiClient {\n readonly #baseUrl: string;\n readonly #authHeader: string;\n readonly #fetch: typeof fetch;\n\n constructor(config: JiraApiClientConfig) {\n if (!config.baseUrl) {\n throw new Error('JiraApiClient is missing required config: baseUrl.');\n }\n // Fail fast on a malformed base URL: a bare host like `acme.atlassian.net`\n // would otherwise surface later as an opaque `TypeError: Invalid URL`.\n let parsed: URL;\n try {\n parsed = new URL(config.baseUrl);\n } catch {\n throw new Error(`JiraApiClient baseUrl is not an absolute URL: \"${config.baseUrl}\".`);\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`JiraApiClient baseUrl must be an http(s) URL: \"${config.baseUrl}\".`);\n }\n if (config.accessToken !== undefined) {\n if (!config.accessToken) {\n throw new Error('JiraApiClient is missing required config: accessToken.');\n }\n this.#authHeader = `Bearer ${config.accessToken}`;\n } else {\n const missing = (['email', 'apiToken'] as const).filter(key => !config[key]);\n if (missing.length > 0) {\n throw new Error(`JiraApiClient is missing required config: ${missing.join(', ')}.`);\n }\n // Basic credentials ride on every request, so never send them over\n // plaintext to a remote host. Loopback stays allowed for local mocks.\n if (parsed.protocol === 'http:' && !LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error('JiraApiClient baseUrl must use https when Basic credentials are configured.');\n }\n this.#authHeader = `Basic ${Buffer.from(`${config.email}:${config.apiToken}`).toString('base64')}`;\n }\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this.#fetch = config.fetchImpl ?? globalThis.fetch;\n }\n\n /** Normalized site base URL (no trailing slash) — used for `/browse/<key>` links. */\n get baseUrl(): string {\n return this.#baseUrl;\n }\n\n async #request<T>(\n method: 'GET' | 'POST',\n path: string,\n options: { query?: Record<string, string | number | undefined>; body?: unknown } = {},\n ): Promise<T> {\n const url = new URL(`${this.#baseUrl}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n const hasBody = options.body !== undefined;\n const res = await this.#fetch(url, {\n method,\n signal: AbortSignal.timeout(JIRA_TIMEOUT_MS),\n headers: {\n authorization: this.#authHeader,\n accept: 'application/json',\n ...(hasBody ? { 'content-type': 'application/json' } : {}),\n },\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n });\n if (!res.ok) {\n // Jira reports validation failures as `errorMessages` and `errors`;\n // the Platform proxy reports upstream failures through `detail`/`error`/`title`.\n let detail: string | null = null;\n try {\n const errBody = (await res.json()) as {\n errorMessages?: string[];\n errors?: Record<string, string>;\n detail?: string;\n error?: string;\n title?: string;\n };\n detail =\n errBody.errorMessages?.[0] ??\n Object.values(errBody.errors ?? {})[0] ??\n errBody.detail ??\n errBody.error ??\n errBody.title ??\n null;\n } catch {\n // Non-JSON error body; fall back to the status code alone.\n }\n throw new JiraApiError(`Jira API request failed (${res.status})${detail ? `: ${detail}` : ''}`, res.status);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n async getServerInfo(): Promise<JiraServerInfo> {\n return this.#request<JiraServerInfo>('GET', '/rest/api/3/serverInfo');\n }\n\n /** One page of the site's projects (Settings intake-source picker). */\n async listProjects(options: { startAt?: number } = {}): Promise<JiraProjectSearchPage> {\n return this.#request<JiraProjectSearchPage>('GET', '/rest/api/3/project/search', {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_PROJECTS_PAGE_SIZE },\n });\n }\n\n /** One page of a JQL search with explicitly requested fields. */\n async searchIssues(options: {\n jql: string;\n fields?: readonly string[];\n nextPageToken?: string;\n maxResults?: number;\n }): Promise<JiraSearchPage> {\n return this.#request<JiraSearchPage>('POST', '/rest/api/3/search/jql', {\n body: {\n jql: options.jql,\n fields: [...(options.fields ?? JIRA_ISSUE_FIELDS)],\n maxResults: options.maxResults ?? JIRA_ISSUES_PAGE_SIZE,\n ...(options.nextPageToken ? { nextPageToken: options.nextPageToken } : {}),\n },\n });\n }\n\n /** Full issue detail by key (`ENG-42`) or id. */\n async getIssue(keyOrId: string): Promise<JiraIssue> {\n return this.#request<JiraIssue>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}`, {\n query: { fields: JIRA_ISSUE_FIELDS.join(',') },\n });\n }\n\n /** One page of an issue's comments (oldest first, Jira default order). */\n async listComments(keyOrId: string, options: { startAt?: number } = {}): Promise<JiraCommentPage> {\n return this.#request<JiraCommentPage>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_COMMENTS_PAGE_SIZE },\n });\n }\n\n /** Post a plain-text comment (wrapped into a minimal ADF document). */\n async createComment(keyOrId: string, body: string): Promise<JiraComment> {\n return this.#request<JiraComment>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n body: { body: textToAdf(body) },\n });\n }\n\n /** Transitions currently legal for the issue (workflow-dependent). */\n async listTransitions(keyOrId: string): Promise<JiraTransition[]> {\n const data = await this.#request<{ transitions?: JiraTransition[] }>(\n 'GET',\n `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`,\n );\n return data.transitions ?? [];\n }\n\n /** Apply a transition by id (Jira responds 204 on success). */\n async applyTransition(keyOrId: string, transitionId: string): Promise<void> {\n await this.#request<void>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`, {\n body: { transition: { id: transitionId } },\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.js","names":["#baseUrl","#authHeader","#projectId","#fetch","#request"],"sources":["../../../src/integrations/jira/api.ts"],"sourcesContent":["/**\n * Typed Jira Cloud REST client (API v3).\n *\n * Shared by the direct and Platform-managed Jira integrations. Direct Jira\n * uses Basic auth against an Atlassian site; Platform Jira points the same\n * client at a connection proxy with a bearer token. Both modes use the same\n * request shapes, timeout, and error normalization.\n *\n * Endpoint notes (Jira Cloud, 2026):\n * - Issue search is `POST /rest/api/3/search/jql` — the legacy\n * `GET /rest/api/3/search` endpoint has been removed. The new endpoint\n * returns only ids unless `fields` is requested explicitly, and pages with\n * an opaque `nextPageToken` cursor (no `startAt`/`total`).\n * - Project listing is `GET /rest/api/3/project/search` with classic\n * `startAt`/`isLast` paging.\n * - Descriptions and comment bodies are ADF documents (see `adf.ts`).\n */\n\nimport type { AdfNode } from './adf.js';\nimport { textToAdf } from './adf.js';\n\nconst JIRA_TIMEOUT_MS = 15_000;\n/** Hosts allowed to receive Basic credentials over plain http (local development mocks). */\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);\n/** Issue page size — Linear parity (`LINEAR_ISSUES_PAGE_SIZE`). */\nexport const JIRA_ISSUES_PAGE_SIZE = 30;\n/** Comment page size — Linear parity (`LINEAR_COMMENTS_PAGE_SIZE`). */\nexport const JIRA_COMMENTS_PAGE_SIZE = 50;\nexport const JIRA_PROJECTS_PAGE_SIZE = 50;\n\n/** Fields requested from issue reads — `search/jql` returns ids only without an explicit list. */\nexport const JIRA_ISSUE_FIELDS = [\n 'summary',\n 'description',\n 'status',\n 'assignee',\n 'reporter',\n 'labels',\n 'priority',\n 'issuetype',\n 'project',\n 'created',\n 'updated',\n] as const;\n\ninterface JiraApiClientBaseConfig {\n /** Jira API base URL, either the Atlassian site or a Platform connection proxy. */\n baseUrl: string;\n fetchImpl?: typeof fetch;\n}\n\nexport type JiraApiClientConfig = JiraApiClientBaseConfig &\n (\n | {\n /** Atlassian account email the API token belongs to. */\n email: string;\n /** API token from id.atlassian.com → Security → API tokens. */\n apiToken: string;\n accessToken?: never;\n }\n | {\n /** Bearer token used when the base URL is a Mastra Platform connection proxy. */\n accessToken: string;\n /**\n * Mastra project id sent as `x-mastra-project-id` so the platform can\n * attribute proxied Jira activity to the project. Falls back to the\n * `MASTRA_PROJECT_ID` environment variable. The platform strips the\n * header before forwarding the request to Atlassian.\n */\n projectId?: string;\n email?: never;\n apiToken?: never;\n }\n );\n\n/** Stable error code the routes/tools surface to the SPA and agents. */\nexport type JiraApiErrorCode = 'jira_auth_failed' | 'jira_request_failed';\n\n/** Normalized Jira API failure carrying the HTTP status and Jira's error detail. */\nexport class JiraApiError extends Error {\n readonly status: number | null;\n readonly code: JiraApiErrorCode;\n\n constructor(message: string, status: number | null) {\n super(message);\n this.name = 'JiraApiError';\n this.status = status;\n this.code = status === 401 || status === 403 ? 'jira_auth_failed' : 'jira_request_failed';\n }\n}\n\nexport interface JiraStatusCategory {\n /** `new` | `indeterminate` | `done` — Jira's fixed status families. */\n key: string;\n name?: string;\n}\n\nexport interface JiraStatus {\n name: string;\n statusCategory?: JiraStatusCategory;\n}\n\nexport interface JiraProject {\n id: string;\n key: string;\n name: string;\n}\n\nexport interface JiraProjectSearchPage {\n values: JiraProject[];\n startAt: number;\n isLast: boolean;\n}\n\nexport interface JiraServerInfo {\n baseUrl: string;\n serverTitle?: string;\n}\n\nexport interface JiraUser {\n displayName: string;\n}\n\nexport interface JiraComment {\n id: string;\n author?: JiraUser | null;\n body?: AdfNode | null;\n created: string;\n}\n\nexport interface JiraCommentPage {\n comments: JiraComment[];\n startAt: number;\n maxResults: number;\n total: number;\n}\n\nexport interface JiraIssueFields {\n summary?: string;\n description?: AdfNode | null;\n status?: JiraStatus;\n assignee?: JiraUser | null;\n reporter?: JiraUser | null;\n labels?: string[];\n priority?: { name: string } | null;\n issuetype?: { name: string };\n project?: { id: string; key: string; name?: string };\n created?: string;\n updated?: string;\n}\n\nexport interface JiraIssue {\n id: string;\n key: string;\n fields: JiraIssueFields;\n}\n\nexport interface JiraSearchPage {\n issues: JiraIssue[];\n /** Opaque cursor for the next page; absent on the last page. */\n nextPageToken?: string | null;\n}\n\nexport interface JiraTransition {\n id: string;\n name: string;\n /** Target status the transition moves the issue to. */\n to?: JiraStatus;\n}\n\nexport class JiraApiClient {\n readonly #baseUrl: string;\n readonly #authHeader: string;\n readonly #projectId: string | null;\n readonly #fetch: typeof fetch;\n\n constructor(config: JiraApiClientConfig) {\n if (!config.baseUrl) {\n throw new Error('JiraApiClient is missing required config: baseUrl.');\n }\n // Fail fast on a malformed base URL: a bare host like `acme.atlassian.net`\n // would otherwise surface later as an opaque `TypeError: Invalid URL`.\n let parsed: URL;\n try {\n parsed = new URL(config.baseUrl);\n } catch {\n throw new Error(`JiraApiClient baseUrl is not an absolute URL: \"${config.baseUrl}\".`);\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`JiraApiClient baseUrl must be an http(s) URL: \"${config.baseUrl}\".`);\n }\n if (config.accessToken !== undefined) {\n if (!config.accessToken) {\n throw new Error('JiraApiClient is missing required config: accessToken.');\n }\n this.#authHeader = `Bearer ${config.accessToken}`;\n this.#projectId = (config.projectId ?? process.env.MASTRA_PROJECT_ID)?.trim() || null;\n } else {\n const missing = (['email', 'apiToken'] as const).filter(key => !config[key]);\n if (missing.length > 0) {\n throw new Error(`JiraApiClient is missing required config: ${missing.join(', ')}.`);\n }\n // Basic credentials ride on every request, so never send them over\n // plaintext to a remote host. Loopback stays allowed for local mocks.\n if (parsed.protocol === 'http:' && !LOOPBACK_HOSTS.has(parsed.hostname)) {\n throw new Error('JiraApiClient baseUrl must use https when Basic credentials are configured.');\n }\n this.#authHeader = `Basic ${Buffer.from(`${config.email}:${config.apiToken}`).toString('base64')}`;\n // Direct Atlassian mode: never send Mastra platform headers to Jira itself.\n this.#projectId = null;\n }\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this.#fetch = config.fetchImpl ?? globalThis.fetch;\n }\n\n /** Normalized site base URL (no trailing slash) — used for `/browse/<key>` links. */\n get baseUrl(): string {\n return this.#baseUrl;\n }\n\n async #request<T>(\n method: 'GET' | 'POST',\n path: string,\n options: { query?: Record<string, string | number | undefined>; body?: unknown } = {},\n ): Promise<T> {\n const url = new URL(`${this.#baseUrl}${path}`);\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n const hasBody = options.body !== undefined;\n const res = await this.#fetch(url, {\n method,\n signal: AbortSignal.timeout(JIRA_TIMEOUT_MS),\n headers: {\n authorization: this.#authHeader,\n accept: 'application/json',\n // Project attribution for the Mastra platform proxy; stripped by the\n // platform before the request reaches Atlassian.\n ...(this.#projectId ? { 'x-mastra-project-id': this.#projectId } : {}),\n ...(hasBody ? { 'content-type': 'application/json' } : {}),\n },\n ...(hasBody ? { body: JSON.stringify(options.body) } : {}),\n });\n if (!res.ok) {\n // Jira reports validation failures as `errorMessages` and `errors`;\n // the Platform proxy reports upstream failures through `detail`/`error`/`title`.\n let detail: string | null = null;\n try {\n const errBody = (await res.json()) as {\n errorMessages?: string[];\n errors?: Record<string, string>;\n detail?: string;\n error?: string;\n title?: string;\n };\n detail =\n errBody.errorMessages?.[0] ??\n Object.values(errBody.errors ?? {})[0] ??\n errBody.detail ??\n errBody.error ??\n errBody.title ??\n null;\n } catch {\n // Non-JSON error body; fall back to the status code alone.\n }\n throw new JiraApiError(`Jira API request failed (${res.status})${detail ? `: ${detail}` : ''}`, res.status);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n\n async getServerInfo(): Promise<JiraServerInfo> {\n return this.#request<JiraServerInfo>('GET', '/rest/api/3/serverInfo');\n }\n\n /** One page of the site's projects (Settings intake-source picker). */\n async listProjects(options: { startAt?: number } = {}): Promise<JiraProjectSearchPage> {\n return this.#request<JiraProjectSearchPage>('GET', '/rest/api/3/project/search', {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_PROJECTS_PAGE_SIZE },\n });\n }\n\n /** One page of a JQL search with explicitly requested fields. */\n async searchIssues(options: {\n jql: string;\n fields?: readonly string[];\n nextPageToken?: string;\n maxResults?: number;\n }): Promise<JiraSearchPage> {\n return this.#request<JiraSearchPage>('POST', '/rest/api/3/search/jql', {\n body: {\n jql: options.jql,\n fields: [...(options.fields ?? JIRA_ISSUE_FIELDS)],\n maxResults: options.maxResults ?? JIRA_ISSUES_PAGE_SIZE,\n ...(options.nextPageToken ? { nextPageToken: options.nextPageToken } : {}),\n },\n });\n }\n\n /** Full issue detail by key (`ENG-42`) or id. */\n async getIssue(keyOrId: string): Promise<JiraIssue> {\n return this.#request<JiraIssue>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}`, {\n query: { fields: JIRA_ISSUE_FIELDS.join(',') },\n });\n }\n\n /** One page of an issue's comments (oldest first, Jira default order). */\n async listComments(keyOrId: string, options: { startAt?: number } = {}): Promise<JiraCommentPage> {\n return this.#request<JiraCommentPage>('GET', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n query: { startAt: options.startAt ?? 0, maxResults: JIRA_COMMENTS_PAGE_SIZE },\n });\n }\n\n /** Post a plain-text comment (wrapped into a minimal ADF document). */\n async createComment(keyOrId: string, body: string): Promise<JiraComment> {\n return this.#request<JiraComment>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/comment`, {\n body: { body: textToAdf(body) },\n });\n }\n\n /** Transitions currently legal for the issue (workflow-dependent). */\n async listTransitions(keyOrId: string): Promise<JiraTransition[]> {\n const data = await this.#request<{ transitions?: JiraTransition[] }>(\n 'GET',\n `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`,\n );\n return data.transitions ?? [];\n }\n\n /** Apply a transition by id (Jira responds 204 on success). */\n async applyTransition(keyOrId: string, transitionId: string): Promise<void> {\n await this.#request<void>('POST', `/rest/api/3/issue/${encodeURIComponent(keyOrId)}/transitions`, {\n body: { transition: { id: transitionId } },\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/agent-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,iBAAS,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM;;yIA+BzE;AAED,iBAAS,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM;;;8IAsCxE;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,cAAc,EACd,MAAM,GACP,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,gEAAgE;IAChE,MAAM,EAAE,iBAAiB,CAAC;CAC3B,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/agent-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,iBAAS,wBAAwB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM;;yIA+BzE;AAED,iBAAS,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM;;;8IAsCxE;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,EAC1C,cAAc,EACd,MAAM,GACP,EAAE;IACD,cAAc,EAAE,cAAc,CAAC;IAC/B,gEAAgE;IAChE,MAAM,EAAE,iBAAiB,CAAC;CAC3B,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC,CAyBpH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-tools.js","names":[],"sources":["../../../src/integrations/linear/agent-tools.ts"],"sourcesContent":["/**\n * Linear tools exposed to the coding agent.\n *\n * Wired into the agent through the SDK's async `extraTools` provider: on each\n * tool-set resolution we map the session's factory project to its owning org\n * and only expose the Linear tools when that org has a Linear connection.\n * Projects whose org never connected Linear (or when the\n * feature is disabled) see no Linear tools at all — the model is never shown\n * tools it can't use.\n *\n * Tenancy mirrors the Linear API routes: everything is scoped by the org that\n * owns the project, and tokens are refreshed through the integration's shared\n * connection lifecycle.\n */\n\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { LinearIntegration } from './integration.js';\nimport { LinearReauthRequiredError } from './integration.js';\n\nfunction createLinearGetIssueTool(linear: LinearIntegration, orgId: string) {\n return createTool({\n id: 'linear_get_issue',\n description:\n \"Fetch a Linear issue's full details — title, description, state, assignee, labels, priority, and discussion comments. Use this whenever you're working on a Linear issue (e.g. ENG-123) to get its complete context.\",\n inputSchema: z.object({\n issue: z.string().min(1).describe('The Linear issue identifier (e.g. \"ENG-123\") or issue UUID.'),\n }),\n execute: async ({ issue }: { issue: string }) => {\n const connection = await linear.loadConnection(orgId);\n if (!connection) {\n return { error: 'Linear is not connected for this repository. Connect Linear in Settings to fetch issues.' };\n }\n try {\n const accessToken = await linear.getFreshAccessToken(connection);\n const detail = await linear.intake.getIssue({\n connection: { type: 'oauth', accessToken },\n issueId: issue.trim(),\n });\n if (!detail) {\n return { error: `Linear issue \"${issue}\" was not found in this workspace.` };\n }\n return detail;\n } catch (err) {\n if (err instanceof LinearReauthRequiredError) {\n return { error: err.message };\n }\n return { error: `Failed to fetch Linear issue: ${err instanceof Error ? err.message : String(err)}` };\n }\n },\n });\n}\n\nfunction createLinearCommentTool(linear: LinearIntegration, orgId: string) {\n return createTool({\n id: 'linear_create_comment',\n description:\n 'Post a comment on a Linear issue (e.g. to report investigation findings, link a PR, or ask a clarifying question). The comment is posted as the connected Linear integration, so make clear it comes from the agent.',\n inputSchema: z.object({\n issue: z.string().min(1).describe('The Linear issue identifier (e.g. \"ENG-123\") or issue UUID.'),\n body: z.string().min(1).describe('The comment body, as Linear-flavored markdown.'),\n }),\n execute: async ({ issue, body }: { issue: string; body: string }) => {\n const connection = await linear.loadConnection(orgId);\n if (!connection) {\n return { error: 'Linear is not connected for this repository. Connect Linear in Settings to post comments.' };\n }\n if (!linear.canPostComments(connection)) {\n return {\n error: 'The Linear connection does not have comment permissions. Reconnect Linear in Settings to grant them.',\n };\n }\n try {\n const accessToken = await linear.getFreshAccessToken(connection);\n const comment = await linear.intake.createComment({\n connection: { type: 'oauth', accessToken },\n issueId: issue.trim(),\n body,\n });\n if (!comment) {\n return { error: `Linear issue \"${issue}\" was not found in this workspace.` };\n }\n return { posted: true, url: comment.url };\n } catch (err) {\n if (err instanceof LinearReauthRequiredError) {\n return { error: err.message };\n }\n return { error: `Failed to post Linear comment: ${err instanceof Error ? err.message : String(err)}` };\n }\n },\n });\n}\n\n/**\n * Async `extraTools` provider: expose Linear tools only when the session's\n * project belongs to an org with an active Linear connection.\n */\nexport async function buildLinearAgentTools({\n requestContext,\n linear,\n}: {\n requestContext: RequestContext;\n /** The integration instance providing the Linear API client. */\n linear: LinearIntegration;\n}): Promise<Record<string, ReturnType<typeof createLinearGetIssueTool> | ReturnType<typeof createLinearCommentTool>>> {\n if (!linear.authEnabled) return {};\n\n const ctx = requestContext.get('controller') as\n AgentControllerRequestContext<{ factoryProjectId?: string }> | undefined;\n if (!ctx) return {};\n\n //
|
|
1
|
+
{"version":3,"file":"agent-tools.js","names":[],"sources":["../../../src/integrations/linear/agent-tools.ts"],"sourcesContent":["/**\n * Linear tools exposed to the coding agent.\n *\n * Wired into the agent through the SDK's async `extraTools` provider: on each\n * tool-set resolution we map the session's factory project to its owning org\n * and only expose the Linear tools when that org has a Linear connection.\n * Projects whose org never connected Linear (or when the\n * feature is disabled) see no Linear tools at all — the model is never shown\n * tools it can't use.\n *\n * Tenancy mirrors the Linear API routes: everything is scoped by the org that\n * owns the project, and tokens are refreshed through the integration's shared\n * connection lifecycle.\n */\n\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { LinearIntegration } from './integration.js';\nimport { LinearReauthRequiredError } from './integration.js';\n\nfunction createLinearGetIssueTool(linear: LinearIntegration, orgId: string) {\n return createTool({\n id: 'linear_get_issue',\n description:\n \"Fetch a Linear issue's full details — title, description, state, assignee, labels, priority, and discussion comments. Use this whenever you're working on a Linear issue (e.g. ENG-123) to get its complete context.\",\n inputSchema: z.object({\n issue: z.string().min(1).describe('The Linear issue identifier (e.g. \"ENG-123\") or issue UUID.'),\n }),\n execute: async ({ issue }: { issue: string }) => {\n const connection = await linear.loadConnection(orgId);\n if (!connection) {\n return { error: 'Linear is not connected for this repository. Connect Linear in Settings to fetch issues.' };\n }\n try {\n const accessToken = await linear.getFreshAccessToken(connection);\n const detail = await linear.intake.getIssue({\n connection: { type: 'oauth', accessToken },\n issueId: issue.trim(),\n });\n if (!detail) {\n return { error: `Linear issue \"${issue}\" was not found in this workspace.` };\n }\n return detail;\n } catch (err) {\n if (err instanceof LinearReauthRequiredError) {\n return { error: err.message };\n }\n return { error: `Failed to fetch Linear issue: ${err instanceof Error ? err.message : String(err)}` };\n }\n },\n });\n}\n\nfunction createLinearCommentTool(linear: LinearIntegration, orgId: string) {\n return createTool({\n id: 'linear_create_comment',\n description:\n 'Post a comment on a Linear issue (e.g. to report investigation findings, link a PR, or ask a clarifying question). The comment is posted as the connected Linear integration, so make clear it comes from the agent.',\n inputSchema: z.object({\n issue: z.string().min(1).describe('The Linear issue identifier (e.g. \"ENG-123\") or issue UUID.'),\n body: z.string().min(1).describe('The comment body, as Linear-flavored markdown.'),\n }),\n execute: async ({ issue, body }: { issue: string; body: string }) => {\n const connection = await linear.loadConnection(orgId);\n if (!connection) {\n return { error: 'Linear is not connected for this repository. Connect Linear in Settings to post comments.' };\n }\n if (!linear.canPostComments(connection)) {\n return {\n error: 'The Linear connection does not have comment permissions. Reconnect Linear in Settings to grant them.',\n };\n }\n try {\n const accessToken = await linear.getFreshAccessToken(connection);\n const comment = await linear.intake.createComment({\n connection: { type: 'oauth', accessToken },\n issueId: issue.trim(),\n body,\n });\n if (!comment) {\n return { error: `Linear issue \"${issue}\" was not found in this workspace.` };\n }\n return { posted: true, url: comment.url };\n } catch (err) {\n if (err instanceof LinearReauthRequiredError) {\n return { error: err.message };\n }\n return { error: `Failed to post Linear comment: ${err instanceof Error ? err.message : String(err)}` };\n }\n },\n });\n}\n\n/**\n * Async `extraTools` provider: expose Linear tools only when the session's\n * project belongs to an org with an active Linear connection.\n */\nexport async function buildLinearAgentTools({\n requestContext,\n linear,\n}: {\n requestContext: RequestContext;\n /** The integration instance providing the Linear API client. */\n linear: LinearIntegration;\n}): Promise<Record<string, ReturnType<typeof createLinearGetIssueTool> | ReturnType<typeof createLinearCommentTool>>> {\n if (!linear.authEnabled) return {};\n\n const ctx = requestContext.get('controller') as\n AgentControllerRequestContext<{ factoryProjectId?: string }> | undefined;\n if (!ctx) return {};\n\n // Prefer the Factory recorded on the session. Factory sessions run under the\n // Factory as their resource, so the resource is the fallback; a legacy\n // session whose resource is its own id resolves to no project.\n const projectId = ctx.getState().factoryProjectId ?? ctx.resourceId;\n if (!projectId) return {};\n\n const orgId = await linear.resolveOrgId(projectId);\n if (!orgId) return {};\n const check = await linear.checkConnection(orgId);\n if (!check.connected) return {};\n\n return {\n linear_get_issue: createLinearGetIssueTool(linear, orgId),\n // Only offered when the granted OAuth scope allows posting comments —\n // connections made before `comments:create` was requested are read-only\n // until the org reconnects Linear.\n ...(check.canComment ? { linear_create_comment: createLinearCommentTool(linear, orgId) } : {}),\n };\n}\n"],"mappings":";;;;AAuBA,SAAS,yBAAyB,QAA2B,OAAe;CAC1E,OAAO,WAAW;EAChB,IAAI;EACJ,aACE;EACF,aAAa,EAAE,OAAO,EACpB,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,+DAA6D,EACjG,CAAC;EACD,SAAS,OAAO,EAAE,YAA+B;GAC/C,MAAM,aAAa,MAAM,OAAO,eAAe,KAAK;GACpD,IAAI,CAAC,YACH,OAAO,EAAE,OAAO,2FAA2F;GAE7G,IAAI;IACF,MAAM,cAAc,MAAM,OAAO,oBAAoB,UAAU;IAC/D,MAAM,SAAS,MAAM,OAAO,OAAO,SAAS;KAC1C,YAAY;MAAE,MAAM;MAAS;KAAY;KACzC,SAAS,MAAM,KAAK;IACtB,CAAC;IACD,IAAI,CAAC,QACH,OAAO,EAAE,OAAO,iBAAiB,MAAM,oCAAoC;IAE7E,OAAO;GACT,SAAS,KAAK;IACZ,IAAI,eAAe,2BACjB,OAAO,EAAE,OAAO,IAAI,QAAQ;IAE9B,OAAO,EAAE,OAAO,iCAAiC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,IAAI;GACtG;EACF;CACF,CAAC;AACH;AAEA,SAAS,wBAAwB,QAA2B,OAAe;CACzE,OAAO,WAAW;EAChB,IAAI;EACJ,aACE;EACF,aAAa,EAAE,OAAO;GACpB,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,+DAA6D;GAC/F,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,gDAAgD;EACnF,CAAC;EACD,SAAS,OAAO,EAAE,OAAO,WAA4C;GACnE,MAAM,aAAa,MAAM,OAAO,eAAe,KAAK;GACpD,IAAI,CAAC,YACH,OAAO,EAAE,OAAO,4FAA4F;GAE9G,IAAI,CAAC,OAAO,gBAAgB,UAAU,GACpC,OAAO,EACL,OAAO,uGACT;GAEF,IAAI;IACF,MAAM,cAAc,MAAM,OAAO,oBAAoB,UAAU;IAC/D,MAAM,UAAU,MAAM,OAAO,OAAO,cAAc;KAChD,YAAY;MAAE,MAAM;MAAS;KAAY;KACzC,SAAS,MAAM,KAAK;KACpB;IACF,CAAC;IACD,IAAI,CAAC,SACH,OAAO,EAAE,OAAO,iBAAiB,MAAM,oCAAoC;IAE7E,OAAO;KAAE,QAAQ;KAAM,KAAK,QAAQ;IAAI;GAC1C,SAAS,KAAK;IACZ,IAAI,eAAe,2BACjB,OAAO,EAAE,OAAO,IAAI,QAAQ;IAE9B,OAAO,EAAE,OAAO,kCAAkC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,IAAI;GACvG;EACF;CACF,CAAC;AACH;;;;;AAMA,eAAsB,sBAAsB,EAC1C,gBACA,UAKoH;CACpH,IAAI,CAAC,OAAO,aAAa,OAAO,CAAC;CAEjC,MAAM,MAAM,eAAe,IAAI,YAAY;CAE3C,IAAI,CAAC,KAAK,OAAO,CAAC;CAKlB,MAAM,YAAY,IAAI,SAAS,CAAC,CAAC,oBAAoB,IAAI;CACzD,IAAI,CAAC,WAAW,OAAO,CAAC;CAExB,MAAM,QAAQ,MAAM,OAAO,aAAa,SAAS;CACjD,IAAI,CAAC,OAAO,OAAO,CAAC;CACpB,MAAM,QAAQ,MAAM,OAAO,gBAAgB,KAAK;CAChD,IAAI,CAAC,MAAM,WAAW,OAAO,CAAC;CAE9B,OAAO;EACL,kBAAkB,yBAAyB,QAAQ,KAAK;EAIxD,GAAI,MAAM,aAAa,EAAE,uBAAuB,wBAAwB,QAAQ,KAAK,EAAE,IAAI,CAAC;CAC9F;AACF"}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export interface PlatformApiClientConfig {
|
|
2
2
|
baseUrl: string;
|
|
3
3
|
accessToken: string;
|
|
4
|
+
/**
|
|
5
|
+
* Mastra project this deployment belongs to. Sent as `x-mastra-project-id`
|
|
6
|
+
* so the platform can attribute proxied provider activity to the project;
|
|
7
|
+
* the platform strips the header before forwarding anything upstream.
|
|
8
|
+
*/
|
|
9
|
+
projectId?: string;
|
|
4
10
|
fetchImpl?: typeof fetch;
|
|
5
11
|
}
|
|
6
12
|
export declare function platformApiClientConfigFromEnv(): PlatformApiClientConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/integrations/platform/api-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAQD,wBAAgB,8BAA8B,IAAI,uBAAuB,
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../../src/integrations/platform/api-client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAQD,wBAAgB,8BAA8B,IAAI,uBAAuB,CAsBxE;AAuBD,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;gBAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAE,MAAM,GAAG,IAAW;CAMrF;AAED,qBAAa,iBAAiB;;gBAMhB,MAAM,EAAE,uBAAuB;IAWrC,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,EACd,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,GACxD,OAAO,CAAC,CAAC,CAAC;IAkBP,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CAgFzG;AAkBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAEvF;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAEvF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAExF"}
|
|
@@ -8,9 +8,11 @@ function platformApiClientConfigFromEnv() {
|
|
|
8
8
|
const integrationsApiUrl = process.env.MASTRA_INTEGRATIONS_API_URL?.trim() || resolveIntegrationsUrl();
|
|
9
9
|
const accessToken = process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() || process.env.MASTRA_PLATFORM_SECRET_KEY?.trim();
|
|
10
10
|
if (!accessToken) throw new Error("Platform integration: missing required environment variable MASTRA_PLATFORM_ACCESS_TOKEN (or MASTRA_PLATFORM_SECRET_KEY).");
|
|
11
|
+
const projectId = process.env.MASTRA_PROJECT_ID?.trim();
|
|
11
12
|
return {
|
|
12
13
|
baseUrl: normalizeIntegrationsApiUrl(integrationsApiUrl),
|
|
13
|
-
accessToken
|
|
14
|
+
accessToken,
|
|
15
|
+
...projectId ? { projectId } : {}
|
|
14
16
|
};
|
|
15
17
|
}
|
|
16
18
|
/**
|
|
@@ -45,12 +47,14 @@ var PlatformApiError = class extends Error {
|
|
|
45
47
|
var PlatformApiClient = class {
|
|
46
48
|
#baseUrl;
|
|
47
49
|
#accessToken;
|
|
50
|
+
#projectId;
|
|
48
51
|
#fetch;
|
|
49
52
|
constructor(config) {
|
|
50
53
|
const missing = ["baseUrl", "accessToken"].filter((field) => !config[field]);
|
|
51
54
|
if (missing.length > 0) throw new Error(`Platform integration: missing required config field(s): ${missing.join(", ")}.`);
|
|
52
55
|
this.#baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
53
56
|
this.#accessToken = config.accessToken;
|
|
57
|
+
this.#projectId = config.projectId?.trim() || null;
|
|
54
58
|
this.#fetch = config.fetchImpl ?? globalThis.fetch;
|
|
55
59
|
}
|
|
56
60
|
async request(method, path, body, options) {
|
|
@@ -100,6 +104,7 @@ var PlatformApiClient = class {
|
|
|
100
104
|
accept: "application/json",
|
|
101
105
|
authorization: `Bearer ${this.#accessToken}`
|
|
102
106
|
};
|
|
107
|
+
if (this.#projectId) headers["x-mastra-project-id"] = this.#projectId;
|
|
103
108
|
if (options?.actingUserId) headers["x-acting-user-id"] = options.actingUserId;
|
|
104
109
|
const timeoutSignal = AbortSignal.timeout(15e3);
|
|
105
110
|
const init = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","names":["#baseUrl","#accessToken","#fetch","#send"],"sources":["../../../src/integrations/platform/api-client.ts"],"sourcesContent":["export interface PlatformApiClientConfig {\n baseUrl: string;\n accessToken: string;\n fetchImpl?: typeof fetch;\n}\n\nconst DEFAULT_INTEGRATIONS_URL = 'https://integrations.mastra.ai';\nconst REGIONAL_INTEGRATIONS_URLS: Record<'us' | 'eu', string> = {\n us: 'https://integrations.us.mastra.ai',\n eu: 'https://integrations.eu.mastra.ai',\n};\n\nexport function platformApiClientConfigFromEnv(): PlatformApiClientConfig {\n // MASTRA_INTEGRATIONS_API_URL is the dedicated override for the\n // integrations service and takes precedence over MASTRA_PLATFORM_REGION.\n // MASTRA_SHARED_API_URL is deliberately not consulted: it configures the\n // shared platform API, and integrations routing is independent of it.\n const integrationsApiUrl = process.env.MASTRA_INTEGRATIONS_API_URL?.trim() || resolveIntegrationsUrl();\n // MASTRA_PLATFORM_ACCESS_TOKEN is the credential Mastra Platform injects\n // into deployed projects; MASTRA_PLATFORM_SECRET_KEY is the org secret key\n // written by project scaffolding. The platform API accepts both forms.\n const accessToken =\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() || process.env.MASTRA_PLATFORM_SECRET_KEY?.trim();\n if (!accessToken) {\n throw new Error(\n 'Platform integration: missing required environment variable MASTRA_PLATFORM_ACCESS_TOKEN (or MASTRA_PLATFORM_SECRET_KEY).',\n );\n }\n return { baseUrl: normalizeIntegrationsApiUrl(integrationsApiUrl), accessToken };\n}\n\n/**\n * Resolves the integrations API base URL from `MASTRA_PLATFORM_REGION`\n * (case-insensitive `us` or `eu`), falling back to the global default.\n * Unknown region values fall through to the global default.\n */\nfunction resolveIntegrationsUrl(): string {\n const region = process.env.MASTRA_PLATFORM_REGION?.trim().toLowerCase();\n if (region === 'us' || region === 'eu') return REGIONAL_INTEGRATIONS_URLS[region];\n return DEFAULT_INTEGRATIONS_URL;\n}\n\n/**\n * Normalizes an integrations API URL to a bare origin. Callers pass fully\n * versioned paths (`/v1/server/...`, `/v2/...`), so a trailing `/v1` — as in\n * legacy platform API URLs like `https://platform.mastra.ai/v1` — is stripped\n * to avoid duplicated version segments.\n */\nfunction normalizeIntegrationsApiUrl(integrationsApiUrl: string): string {\n return integrationsApiUrl.replace(/\\/+$/, '').replace(/\\/v1$/, '');\n}\n\nexport class PlatformApiError extends Error {\n readonly status: number;\n readonly retryAfterSeconds: number | null;\n\n constructor(message: string, status: number, retryAfterSeconds: number | null = null) {\n super(message);\n this.name = 'PlatformApiError';\n this.status = status;\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport class PlatformApiClient {\n readonly #baseUrl: string;\n readonly #accessToken: string;\n readonly #fetch: typeof fetch;\n\n constructor(config: PlatformApiClientConfig) {\n const missing = ['baseUrl', 'accessToken'].filter(field => !config[field as keyof PlatformApiClientConfig]);\n if (missing.length > 0) {\n throw new Error(`Platform integration: missing required config field(s): ${missing.join(', ')}.`);\n }\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this.#accessToken = config.accessToken;\n this.#fetch = config.fetchImpl ?? globalThis.fetch;\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options?: { signal?: AbortSignal; actingUserId?: string },\n ): Promise<T> {\n const response = await this.#send(method, path, body, options);\n if (!response.ok) {\n const message = redact(await extractError(response), this.#accessToken);\n const retryAfterSeconds = parseRetryAfter(response.headers.get('retry-after'));\n logPlatformError('Platform API request failed', {\n method,\n path,\n status: response.status,\n retryAfterSeconds,\n message,\n });\n throw new PlatformApiError(message, response.status, retryAfterSeconds);\n }\n if (response.status === 204) return undefined as T;\n return (await response.json()) as T;\n }\n\n async requestRedirect(method: string, path: string, options?: { signal?: AbortSignal }): Promise<string> {\n const response = await this.#send(method, path, undefined, options, 'manual');\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (location) return location;\n }\n if (!response.ok) {\n const message = redact(await extractError(response), this.#accessToken);\n const retryAfterSeconds = parseRetryAfter(response.headers.get('retry-after'));\n logPlatformError('Platform API redirect request failed', {\n method,\n path,\n status: response.status,\n retryAfterSeconds,\n message,\n });\n throw new PlatformApiError(message, response.status, retryAfterSeconds);\n }\n logPlatformError('Platform API request did not return a redirect', { method, path, status: response.status });\n throw new PlatformApiError('Platform API request did not return a redirect.', response.status);\n }\n\n async #send(\n method: string,\n path: string,\n body?: unknown,\n options?: { signal?: AbortSignal; actingUserId?: string },\n redirect?: RequestInit['redirect'],\n ): Promise<Response> {\n const headers: Record<string, string> = {\n accept: 'application/json',\n authorization: `Bearer ${this.#accessToken}`,\n };\n // Acting end-user for platform GitHub writes: the platform resolves this\n // user's GitHub OAuth connection (org-scoped) so issues/PRs are authored\n // by the human instead of the App bot. Ignored by older platforms.\n if (options?.actingUserId) {\n headers['x-acting-user-id'] = options.actingUserId;\n }\n const timeoutSignal = AbortSignal.timeout(15_000);\n const init: RequestInit = {\n method,\n headers,\n redirect,\n signal: options?.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal,\n };\n if (body !== undefined) {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(body);\n }\n\n try {\n return await this.#fetch(`${this.#baseUrl}${path}`, init);\n } catch (error) {\n if (error instanceof Error && error.message.includes(this.#accessToken)) {\n const redacted = new Error(redact(error.message, this.#accessToken));\n redacted.name = error.name;\n logPlatformError('Platform API transport error', {\n method,\n path,\n name: redacted.name,\n message: redacted.message,\n });\n throw redacted;\n }\n logPlatformError('Platform API transport error', {\n method,\n path,\n name: error instanceof Error ? error.name : undefined,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n}\n\nasync function extractError(response: Response): Promise<string> {\n try {\n const data = (await response.clone().json()) as Record<string, unknown>;\n for (const field of ['detail', 'error', 'title']) {\n if (typeof data[field] === 'string' && data[field]) return data[field];\n }\n } catch {\n // Fall through to the status-based message.\n }\n return `Platform API request failed (${response.status})`;\n}\n\nfunction redact(message: string, accessToken: string): string {\n return message.split(accessToken).join('[REDACTED]');\n}\n\nexport function logPlatformInfo(message: string, fields?: Record<string, unknown>): void {\n writePlatformLog('info', message, fields);\n}\n\nexport function logPlatformWarn(message: string, fields?: Record<string, unknown>): void {\n writePlatformLog('warn', message, fields);\n}\n\nexport function logPlatformError(message: string, fields?: Record<string, unknown>): void {\n writePlatformLog('error', message, fields);\n}\n\nfunction writePlatformLog(level: 'info' | 'warn' | 'error', message: string, fields?: Record<string, unknown>): void {\n const metadata = fields ? ` ${JSON.stringify(stripUndefined(fields))}` : '';\n process.stderr.write(`[Mastra Factory] ${level.toUpperCase()} ${message}${metadata}\\n`);\n}\n\nfunction stripUndefined(fields: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number.parseInt(value, 10);\n return Number.isSafeInteger(seconds) && seconds >= 0 ? seconds : null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-client.js","names":["#baseUrl","#accessToken","#projectId","#fetch","#send"],"sources":["../../../src/integrations/platform/api-client.ts"],"sourcesContent":["export interface PlatformApiClientConfig {\n baseUrl: string;\n accessToken: string;\n /**\n * Mastra project this deployment belongs to. Sent as `x-mastra-project-id`\n * so the platform can attribute proxied provider activity to the project;\n * the platform strips the header before forwarding anything upstream.\n */\n projectId?: string;\n fetchImpl?: typeof fetch;\n}\n\nconst DEFAULT_INTEGRATIONS_URL = 'https://integrations.mastra.ai';\nconst REGIONAL_INTEGRATIONS_URLS: Record<'us' | 'eu', string> = {\n us: 'https://integrations.us.mastra.ai',\n eu: 'https://integrations.eu.mastra.ai',\n};\n\nexport function platformApiClientConfigFromEnv(): PlatformApiClientConfig {\n // MASTRA_INTEGRATIONS_API_URL is the dedicated override for the\n // integrations service and takes precedence over MASTRA_PLATFORM_REGION.\n // MASTRA_SHARED_API_URL is deliberately not consulted: it configures the\n // shared platform API, and integrations routing is independent of it.\n const integrationsApiUrl = process.env.MASTRA_INTEGRATIONS_API_URL?.trim() || resolveIntegrationsUrl();\n // MASTRA_PLATFORM_ACCESS_TOKEN is the credential Mastra Platform injects\n // into deployed projects; MASTRA_PLATFORM_SECRET_KEY is the org secret key\n // written by project scaffolding. The platform API accepts both forms.\n const accessToken =\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() || process.env.MASTRA_PLATFORM_SECRET_KEY?.trim();\n if (!accessToken) {\n throw new Error(\n 'Platform integration: missing required environment variable MASTRA_PLATFORM_ACCESS_TOKEN (or MASTRA_PLATFORM_SECRET_KEY).',\n );\n }\n const projectId = process.env.MASTRA_PROJECT_ID?.trim();\n return {\n baseUrl: normalizeIntegrationsApiUrl(integrationsApiUrl),\n accessToken,\n ...(projectId ? { projectId } : {}),\n };\n}\n\n/**\n * Resolves the integrations API base URL from `MASTRA_PLATFORM_REGION`\n * (case-insensitive `us` or `eu`), falling back to the global default.\n * Unknown region values fall through to the global default.\n */\nfunction resolveIntegrationsUrl(): string {\n const region = process.env.MASTRA_PLATFORM_REGION?.trim().toLowerCase();\n if (region === 'us' || region === 'eu') return REGIONAL_INTEGRATIONS_URLS[region];\n return DEFAULT_INTEGRATIONS_URL;\n}\n\n/**\n * Normalizes an integrations API URL to a bare origin. Callers pass fully\n * versioned paths (`/v1/server/...`, `/v2/...`), so a trailing `/v1` — as in\n * legacy platform API URLs like `https://platform.mastra.ai/v1` — is stripped\n * to avoid duplicated version segments.\n */\nfunction normalizeIntegrationsApiUrl(integrationsApiUrl: string): string {\n return integrationsApiUrl.replace(/\\/+$/, '').replace(/\\/v1$/, '');\n}\n\nexport class PlatformApiError extends Error {\n readonly status: number;\n readonly retryAfterSeconds: number | null;\n\n constructor(message: string, status: number, retryAfterSeconds: number | null = null) {\n super(message);\n this.name = 'PlatformApiError';\n this.status = status;\n this.retryAfterSeconds = retryAfterSeconds;\n }\n}\n\nexport class PlatformApiClient {\n readonly #baseUrl: string;\n readonly #accessToken: string;\n readonly #projectId: string | null;\n readonly #fetch: typeof fetch;\n\n constructor(config: PlatformApiClientConfig) {\n const missing = ['baseUrl', 'accessToken'].filter(field => !config[field as keyof PlatformApiClientConfig]);\n if (missing.length > 0) {\n throw new Error(`Platform integration: missing required config field(s): ${missing.join(', ')}.`);\n }\n this.#baseUrl = config.baseUrl.replace(/\\/+$/, '');\n this.#accessToken = config.accessToken;\n this.#projectId = config.projectId?.trim() || null;\n this.#fetch = config.fetchImpl ?? globalThis.fetch;\n }\n\n async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options?: { signal?: AbortSignal; actingUserId?: string },\n ): Promise<T> {\n const response = await this.#send(method, path, body, options);\n if (!response.ok) {\n const message = redact(await extractError(response), this.#accessToken);\n const retryAfterSeconds = parseRetryAfter(response.headers.get('retry-after'));\n logPlatformError('Platform API request failed', {\n method,\n path,\n status: response.status,\n retryAfterSeconds,\n message,\n });\n throw new PlatformApiError(message, response.status, retryAfterSeconds);\n }\n if (response.status === 204) return undefined as T;\n return (await response.json()) as T;\n }\n\n async requestRedirect(method: string, path: string, options?: { signal?: AbortSignal }): Promise<string> {\n const response = await this.#send(method, path, undefined, options, 'manual');\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get('location');\n if (location) return location;\n }\n if (!response.ok) {\n const message = redact(await extractError(response), this.#accessToken);\n const retryAfterSeconds = parseRetryAfter(response.headers.get('retry-after'));\n logPlatformError('Platform API redirect request failed', {\n method,\n path,\n status: response.status,\n retryAfterSeconds,\n message,\n });\n throw new PlatformApiError(message, response.status, retryAfterSeconds);\n }\n logPlatformError('Platform API request did not return a redirect', { method, path, status: response.status });\n throw new PlatformApiError('Platform API request did not return a redirect.', response.status);\n }\n\n async #send(\n method: string,\n path: string,\n body?: unknown,\n options?: { signal?: AbortSignal; actingUserId?: string },\n redirect?: RequestInit['redirect'],\n ): Promise<Response> {\n const headers: Record<string, string> = {\n accept: 'application/json',\n authorization: `Bearer ${this.#accessToken}`,\n };\n // Project attribution for proxied provider calls. Org-scoped machine\n // tokens carry no project claim, so without this header the platform\n // records proxy activity with no project.\n if (this.#projectId) {\n headers['x-mastra-project-id'] = this.#projectId;\n }\n // Acting end-user for platform GitHub writes: the platform resolves this\n // user's GitHub OAuth connection (org-scoped) so issues/PRs are authored\n // by the human instead of the App bot. Ignored by older platforms.\n if (options?.actingUserId) {\n headers['x-acting-user-id'] = options.actingUserId;\n }\n const timeoutSignal = AbortSignal.timeout(15_000);\n const init: RequestInit = {\n method,\n headers,\n redirect,\n signal: options?.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal,\n };\n if (body !== undefined) {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(body);\n }\n\n try {\n return await this.#fetch(`${this.#baseUrl}${path}`, init);\n } catch (error) {\n if (error instanceof Error && error.message.includes(this.#accessToken)) {\n const redacted = new Error(redact(error.message, this.#accessToken));\n redacted.name = error.name;\n logPlatformError('Platform API transport error', {\n method,\n path,\n name: redacted.name,\n message: redacted.message,\n });\n throw redacted;\n }\n logPlatformError('Platform API transport error', {\n method,\n path,\n name: error instanceof Error ? error.name : undefined,\n message: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n}\n\nasync function extractError(response: Response): Promise<string> {\n try {\n const data = (await response.clone().json()) as Record<string, unknown>;\n for (const field of ['detail', 'error', 'title']) {\n if (typeof data[field] === 'string' && data[field]) return data[field];\n }\n } catch {\n // Fall through to the status-based message.\n }\n return `Platform API request failed (${response.status})`;\n}\n\nfunction redact(message: string, accessToken: string): string {\n return message.split(accessToken).join('[REDACTED]');\n}\n\nexport function logPlatformInfo(message: string, fields?: Record<string, unknown>): void {\n writePlatformLog('info', message, fields);\n}\n\nexport function logPlatformWarn(message: string, fields?: Record<string, unknown>): void {\n writePlatformLog('warn', message, fields);\n}\n\nexport function logPlatformError(message: string, fields?: Record<string, unknown>): void {\n writePlatformLog('error', message, fields);\n}\n\nfunction writePlatformLog(level: 'info' | 'warn' | 'error', message: string, fields?: Record<string, unknown>): void {\n const metadata = fields ? ` ${JSON.stringify(stripUndefined(fields))}` : '';\n process.stderr.write(`[Mastra Factory] ${level.toUpperCase()} ${message}${metadata}\\n`);\n}\n\nfunction stripUndefined(fields: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\nfunction parseRetryAfter(value: string | null): number | null {\n if (!value) return null;\n const seconds = Number.parseInt(value, 10);\n return Number.isSafeInteger(seconds) && seconds >= 0 ? seconds : null;\n}\n"],"mappings":";AAYA,MAAM,2BAA2B;AACjC,MAAM,6BAA0D;CAC9D,IAAI;CACJ,IAAI;AACN;AAEA,SAAgB,iCAA0D;CAKxE,MAAM,qBAAqB,QAAQ,IAAI,6BAA6B,KAAK,KAAK,uBAAuB;CAIrG,MAAM,cACJ,QAAQ,IAAI,8BAA8B,KAAK,KAAK,QAAQ,IAAI,4BAA4B,KAAK;CACnG,IAAI,CAAC,aACH,MAAM,IAAI,MACR,2HACF;CAEF,MAAM,YAAY,QAAQ,IAAI,mBAAmB,KAAK;CACtD,OAAO;EACL,SAAS,4BAA4B,kBAAkB;EACvD;EACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CACnC;AACF;;;;;;AAOA,SAAS,yBAAiC;CACxC,MAAM,SAAS,QAAQ,IAAI,wBAAwB,KAAK,CAAC,CAAC,YAAY;CACtE,IAAI,WAAW,QAAQ,WAAW,MAAM,OAAO,2BAA2B;CAC1E,OAAO;AACT;;;;;;;AAQA,SAAS,4BAA4B,oBAAoC;CACvE,OAAO,mBAAmB,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,SAAS,EAAE;AACnE;AAEA,IAAa,mBAAb,cAAsC,MAAM;CAC1C;CACA;CAEA,YAAY,SAAiB,QAAgB,oBAAmC,MAAM;EACpF,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,oBAAoB;CAC3B;AACF;AAEA,IAAa,oBAAb,MAA+B;CAC7B;CACA;CACA;CACA;CAEA,YAAY,QAAiC;EAC3C,MAAM,UAAU,CAAC,WAAW,aAAa,CAAC,CAAC,QAAO,UAAS,CAAC,OAAO,MAAuC;EAC1G,IAAI,QAAQ,SAAS,GACnB,MAAM,IAAI,MAAM,2DAA2D,QAAQ,KAAK,IAAI,EAAE,EAAE;EAElG,KAAKA,WAAW,OAAO,QAAQ,QAAQ,QAAQ,EAAE;EACjD,KAAKC,eAAe,OAAO;EAC3B,KAAKC,aAAa,OAAO,WAAW,KAAK,KAAK;EAC9C,KAAKC,SAAS,OAAO,aAAa,WAAW;CAC/C;CAEA,MAAM,QACJ,QACA,MACA,MACA,SACY;EACZ,MAAM,WAAW,MAAM,KAAKC,MAAM,QAAQ,MAAM,MAAM,OAAO;EAC7D,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,UAAU,OAAO,MAAM,aAAa,QAAQ,GAAG,KAAKH,YAAY;GACtE,MAAM,oBAAoB,gBAAgB,SAAS,QAAQ,IAAI,aAAa,CAAC;GAC7E,iBAAiB,+BAA+B;IAC9C;IACA;IACA,QAAQ,SAAS;IACjB;IACA;GACF,CAAC;GACD,MAAM,IAAI,iBAAiB,SAAS,SAAS,QAAQ,iBAAiB;EACxE;EACA,IAAI,SAAS,WAAW,KAAK,OAAO,KAAA;EACpC,OAAQ,MAAM,SAAS,KAAK;CAC9B;CAEA,MAAM,gBAAgB,QAAgB,MAAc,SAAqD;EACvG,MAAM,WAAW,MAAM,KAAKG,MAAM,QAAQ,MAAM,KAAA,GAAW,SAAS,QAAQ;EAC5E,IAAI,SAAS,UAAU,OAAO,SAAS,SAAS,KAAK;GACnD,MAAM,WAAW,SAAS,QAAQ,IAAI,UAAU;GAChD,IAAI,UAAU,OAAO;EACvB;EACA,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,UAAU,OAAO,MAAM,aAAa,QAAQ,GAAG,KAAKH,YAAY;GACtE,MAAM,oBAAoB,gBAAgB,SAAS,QAAQ,IAAI,aAAa,CAAC;GAC7E,iBAAiB,wCAAwC;IACvD;IACA;IACA,QAAQ,SAAS;IACjB;IACA;GACF,CAAC;GACD,MAAM,IAAI,iBAAiB,SAAS,SAAS,QAAQ,iBAAiB;EACxE;EACA,iBAAiB,kDAAkD;GAAE;GAAQ;GAAM,QAAQ,SAAS;EAAO,CAAC;EAC5G,MAAM,IAAI,iBAAiB,mDAAmD,SAAS,MAAM;CAC/F;CAEA,MAAMG,MACJ,QACA,MACA,MACA,SACA,UACmB;EACnB,MAAM,UAAkC;GACtC,QAAQ;GACR,eAAe,UAAU,KAAKH;EAChC;EAIA,IAAI,KAAKC,YACP,QAAQ,yBAAyB,KAAKA;EAKxC,IAAI,SAAS,cACX,QAAQ,sBAAsB,QAAQ;EAExC,MAAM,gBAAgB,YAAY,QAAQ,IAAM;EAChD,MAAM,OAAoB;GACxB;GACA;GACA;GACA,QAAQ,SAAS,SAAS,YAAY,IAAI,CAAC,QAAQ,QAAQ,aAAa,CAAC,IAAI;EAC/E;EACA,IAAI,SAAS,KAAA,GAAW;GACtB,QAAQ,kBAAkB;GAC1B,KAAK,OAAO,KAAK,UAAU,IAAI;EACjC;EAEA,IAAI;GACF,OAAO,MAAM,KAAKC,OAAO,GAAG,KAAKH,WAAW,QAAQ,IAAI;EAC1D,SAAS,OAAO;GACd,IAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,KAAKC,YAAY,GAAG;IACvE,MAAM,WAAW,IAAI,MAAM,OAAO,MAAM,SAAS,KAAKA,YAAY,CAAC;IACnE,SAAS,OAAO,MAAM;IACtB,iBAAiB,gCAAgC;KAC/C;KACA;KACA,MAAM,SAAS;KACf,SAAS,SAAS;IACpB,CAAC;IACD,MAAM;GACR;GACA,iBAAiB,gCAAgC;IAC/C;IACA;IACA,MAAM,iBAAiB,QAAQ,MAAM,OAAO,KAAA;IAC5C,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAChE,CAAC;GACD,MAAM;EACR;CACF;AACF;AAEA,eAAe,aAAa,UAAqC;CAC/D,IAAI;EACF,MAAM,OAAQ,MAAM,SAAS,MAAM,CAAC,CAAC,KAAK;EAC1C,KAAK,MAAM,SAAS;GAAC;GAAU;GAAS;EAAO,GAC7C,IAAI,OAAO,KAAK,WAAW,YAAY,KAAK,QAAQ,OAAO,KAAK;CAEpE,QAAQ,CAER;CACA,OAAO,gCAAgC,SAAS,OAAO;AACzD;AAEA,SAAS,OAAO,SAAiB,aAA6B;CAC5D,OAAO,QAAQ,MAAM,WAAW,CAAC,CAAC,KAAK,YAAY;AACrD;AAEA,SAAgB,gBAAgB,SAAiB,QAAwC;CACvF,iBAAiB,QAAQ,SAAS,MAAM;AAC1C;AAEA,SAAgB,gBAAgB,SAAiB,QAAwC;CACvF,iBAAiB,QAAQ,SAAS,MAAM;AAC1C;AAEA,SAAgB,iBAAiB,SAAiB,QAAwC;CACxF,iBAAiB,SAAS,SAAS,MAAM;AAC3C;AAEA,SAAS,iBAAiB,OAAkC,SAAiB,QAAwC;CACnH,MAAM,WAAW,SAAS,IAAI,KAAK,UAAU,eAAe,MAAM,CAAC,MAAM;CACzE,QAAQ,OAAO,MAAM,oBAAoB,MAAM,YAAY,EAAE,GAAG,UAAU,SAAS,GAAG;AACxF;AAEA,SAAS,eAAe,QAA0D;CAChF,OAAO,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC,CAAC,QAAQ,GAAG,WAAW,UAAU,KAAA,CAAS,CAAC;AAC7F;AAEA,SAAS,gBAAgB,OAAqC;CAC5D,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,UAAU,OAAO,SAAS,OAAO,EAAE;CACzC,OAAO,OAAO,cAAc,OAAO,KAAK,WAAW,IAAI,UAAU;AACnE"}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import type { ApiRoute } from '@mastra/core/server';
|
|
15
15
|
import type { RouteAuth } from '../../../routes/route.js';
|
|
16
|
-
import { PlatformApiClient } from '../api-client.js';
|
|
16
|
+
import type { PlatformApiClient } from '../api-client.js';
|
|
17
17
|
export interface PlatformConnectProvider {
|
|
18
18
|
/** Platform integration id used to mint connect sessions. */
|
|
19
19
|
integrationId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/connect/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/connect/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAUzD,MAAM,WAAW,uBAAuB;IACtC,6DAA6D;IAC7D,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,wBAAwB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAI9E,CAAC;AAmBF,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AA2CD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,QAAQ,EAAE,CAgFjG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","names":[],"sources":["../../../../src/integrations/platform/connect/routes.ts"],"sourcesContent":["/**\n * Mastra `apiRoutes` that let the Factory SPA connect and reconnect\n * Platform-managed provider accounts without leaving Factory.\n *\n * The browser cannot talk to the integrations service itself — it has no\n * Platform session — so these routes mint Nango connect/reconnect sessions\n * server-side with the deploy's machine credentials\n * (`MASTRA_PLATFORM_ACCESS_TOKEN`/`MASTRA_PLATFORM_SECRET_KEY`) and hand the\n * short-lived session token back to the SPA, which drives the provider's\n * OAuth popup headlessly via `@nangohq/frontend`. Gating mirrors the\n * Platform's own rule for these providers: a signed-in organization member\n * (org:write parity with Linear), not an organization admin.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { RouteAuth } from '../../../routes/route.js';\nimport { PlatformApiClient, PlatformApiError } from '../api-client.js';\n\ntype RouteContext = Context;\n\n/** Erase a route handler's path-parameterized context to a plain `Context`. */\nfunction loose(c: unknown): RouteContext {\n return c as RouteContext;\n}\n\nexport interface PlatformConnectProvider {\n /** Platform integration id used to mint connect sessions. */\n integrationId: string;\n /** Connection `integrationId` variants that belong to this provider. */\n connectionIntegrationIds: readonly string[];\n}\n\n/**\n * Providers the Factory SPA may connect through these routes. Keys are the\n * SPA-facing provider slugs; `integrationId` is the Platform catalog id used\n * for new connect sessions. Provider-specific PRs extend this registry when\n * their Factory integrations are available.\n */\nexport const PLATFORM_CONNECT_PROVIDERS: Record<string, PlatformConnectProvider> = {\n jira: { integrationId: 'jira', connectionIntegrationIds: ['jira'] },\n 'incident-io': { integrationId: 'incident-io', connectionIntegrationIds: ['incident-io'] },\n gitlab: { integrationId: 'gitlab', connectionIntegrationIds: ['gitlab'] },\n};\n\ninterface PlatformConnectionRow {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n displayName?: string | null;\n connectedAt?: string;\n}\n\ninterface PlatformSessionResponse {\n connectionId: string;\n integrationId: string;\n connectUrl: string;\n sessionToken: string;\n expiresAt: string;\n}\n\nexport interface BuildPlatformConnectRoutesOptions {\n auth: RouteAuth;\n client: PlatformApiClient;\n}\n\nasync function resolveOrgTenant(\n c: RouteContext,\n auth: RouteAuth,\n): Promise<{ tenant: { orgId: string; userId: string } } | { response: Response }> {\n if (!auth.enabled()) {\n return { response: c.json({ error: 'auth_disabled', message: 'Factory auth is not enabled.' }, 403) };\n }\n await auth.ensureUser(c);\n const tenant = auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n {\n error: 'organization_required',\n message: 'Provider connections require an organization. Personal accounts cannot connect providers.',\n },\n 403,\n ),\n };\n }\n return { tenant: { orgId: tenant.orgId, userId: tenant.userId } };\n}\n\nfunction providerFromParam(c: RouteContext): PlatformConnectProvider | null {\n const provider = c.req.param('provider');\n return provider ? (PLATFORM_CONNECT_PROVIDERS[provider] ?? null) : null;\n}\n\nfunction platformError(c: RouteContext, err: unknown) {\n if (err instanceof PlatformApiError) {\n if (err.status === 409) {\n return c.json({ error: 'session_pending', message: err.message }, 409);\n }\n if (err.status === 403) {\n return c.json({ error: 'platform_forbidden', message: err.message }, 403);\n }\n }\n return c.json({ error: 'platform_request_failed', message: err instanceof Error ? err.message : String(err) }, 502);\n}\n\n/**\n * Build the `/web/integrations/platform/*` routes. Callers only mount these\n * when Platform machine credentials are configured.\n */\nexport function buildPlatformConnectRoutes(options: BuildPlatformConnectRoutesOptions): ApiRoute[] {\n const { auth, client } = options;\n\n return [\n registerApiRoute('/web/integrations/platform/:provider/connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n try {\n const result = await client.request<{ connections: PlatformConnectionRow[] }>('GET', '/v2/connections');\n const connections = result.connections.filter(connection =>\n provider.connectionIntegrationIds.includes(connection.integrationId),\n );\n return c.json({ connections });\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n registerApiRoute('/web/integrations/platform/:provider/connect-session', {\n method: 'POST',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n try {\n const session = await client.request<PlatformSessionResponse>(\n 'POST',\n `/v2/integrations/${encodeURIComponent(provider.integrationId)}/connect-sessions`,\n {},\n );\n c.header('Cache-Control', 'no-store');\n return c.json(session, 201);\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n registerApiRoute('/web/integrations/platform/:provider/connections/:connectionId/reconnect-session', {\n method: 'POST',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n const connectionId = c.req.param('connectionId');\n if (!connectionId) return c.json({ error: 'connection_required' }, 400);\n try {\n // Confirm the connection belongs to this provider before minting so\n // one provider's page cannot reconnect a different provider's\n // connection through this route family.\n const listed = await client.request<{ connections: PlatformConnectionRow[] }>('GET', '/v2/connections');\n const connection = listed.connections.find(\n candidate =>\n candidate.id === connectionId && provider.connectionIntegrationIds.includes(candidate.integrationId),\n );\n if (!connection) return c.json({ error: 'connection_not_found' }, 404);\n const session = await client.request<PlatformSessionResponse>(\n 'POST',\n `/v2/connections/${encodeURIComponent(connectionId)}/reconnect-session`,\n {},\n );\n c.header('Cache-Control', 'no-store');\n return c.json(session, 201);\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n ];\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"routes.js","names":[],"sources":["../../../../src/integrations/platform/connect/routes.ts"],"sourcesContent":["/**\n * Mastra `apiRoutes` that let the Factory SPA connect and reconnect\n * Platform-managed provider accounts without leaving Factory.\n *\n * The browser cannot talk to the integrations service itself — it has no\n * Platform session — so these routes mint Nango connect/reconnect sessions\n * server-side with the deploy's machine credentials\n * (`MASTRA_PLATFORM_ACCESS_TOKEN`/`MASTRA_PLATFORM_SECRET_KEY`) and hand the\n * short-lived session token back to the SPA, which drives the provider's\n * OAuth popup headlessly via `@nangohq/frontend`. Gating mirrors the\n * Platform's own rule for these providers: a signed-in organization member\n * (org:write parity with Linear), not an organization admin.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { PlatformApiClient} from '../api-client.js';\nimport { PlatformApiError } from '../api-client.js';\n\ntype RouteContext = Context;\n\n/** Erase a route handler's path-parameterized context to a plain `Context`. */\nfunction loose(c: unknown): RouteContext {\n return c as RouteContext;\n}\n\nexport interface PlatformConnectProvider {\n /** Platform integration id used to mint connect sessions. */\n integrationId: string;\n /** Connection `integrationId` variants that belong to this provider. */\n connectionIntegrationIds: readonly string[];\n}\n\n/**\n * Providers the Factory SPA may connect through these routes. Keys are the\n * SPA-facing provider slugs; `integrationId` is the Platform catalog id used\n * for new connect sessions. Provider-specific PRs extend this registry when\n * their Factory integrations are available.\n */\nexport const PLATFORM_CONNECT_PROVIDERS: Record<string, PlatformConnectProvider> = {\n jira: { integrationId: 'jira', connectionIntegrationIds: ['jira'] },\n 'incident-io': { integrationId: 'incident-io', connectionIntegrationIds: ['incident-io'] },\n gitlab: { integrationId: 'gitlab', connectionIntegrationIds: ['gitlab'] },\n};\n\ninterface PlatformConnectionRow {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n displayName?: string | null;\n connectedAt?: string;\n}\n\ninterface PlatformSessionResponse {\n connectionId: string;\n integrationId: string;\n connectUrl: string;\n sessionToken: string;\n expiresAt: string;\n}\n\nexport interface BuildPlatformConnectRoutesOptions {\n auth: RouteAuth;\n client: PlatformApiClient;\n}\n\nasync function resolveOrgTenant(\n c: RouteContext,\n auth: RouteAuth,\n): Promise<{ tenant: { orgId: string; userId: string } } | { response: Response }> {\n if (!auth.enabled()) {\n return { response: c.json({ error: 'auth_disabled', message: 'Factory auth is not enabled.' }, 403) };\n }\n await auth.ensureUser(c);\n const tenant = auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n {\n error: 'organization_required',\n message: 'Provider connections require an organization. Personal accounts cannot connect providers.',\n },\n 403,\n ),\n };\n }\n return { tenant: { orgId: tenant.orgId, userId: tenant.userId } };\n}\n\nfunction providerFromParam(c: RouteContext): PlatformConnectProvider | null {\n const provider = c.req.param('provider');\n return provider ? (PLATFORM_CONNECT_PROVIDERS[provider] ?? null) : null;\n}\n\nfunction platformError(c: RouteContext, err: unknown) {\n if (err instanceof PlatformApiError) {\n if (err.status === 409) {\n return c.json({ error: 'session_pending', message: err.message }, 409);\n }\n if (err.status === 403) {\n return c.json({ error: 'platform_forbidden', message: err.message }, 403);\n }\n }\n return c.json({ error: 'platform_request_failed', message: err instanceof Error ? err.message : String(err) }, 502);\n}\n\n/**\n * Build the `/web/integrations/platform/*` routes. Callers only mount these\n * when Platform machine credentials are configured.\n */\nexport function buildPlatformConnectRoutes(options: BuildPlatformConnectRoutesOptions): ApiRoute[] {\n const { auth, client } = options;\n\n return [\n registerApiRoute('/web/integrations/platform/:provider/connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n try {\n const result = await client.request<{ connections: PlatformConnectionRow[] }>('GET', '/v2/connections');\n const connections = result.connections.filter(connection =>\n provider.connectionIntegrationIds.includes(connection.integrationId),\n );\n return c.json({ connections });\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n registerApiRoute('/web/integrations/platform/:provider/connect-session', {\n method: 'POST',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n try {\n const session = await client.request<PlatformSessionResponse>(\n 'POST',\n `/v2/integrations/${encodeURIComponent(provider.integrationId)}/connect-sessions`,\n {},\n );\n c.header('Cache-Control', 'no-store');\n return c.json(session, 201);\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n registerApiRoute('/web/integrations/platform/:provider/connections/:connectionId/reconnect-session', {\n method: 'POST',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n const connectionId = c.req.param('connectionId');\n if (!connectionId) return c.json({ error: 'connection_required' }, 400);\n try {\n // Confirm the connection belongs to this provider before minting so\n // one provider's page cannot reconnect a different provider's\n // connection through this route family.\n const listed = await client.request<{ connections: PlatformConnectionRow[] }>('GET', '/v2/connections');\n const connection = listed.connections.find(\n candidate =>\n candidate.id === connectionId && provider.connectionIntegrationIds.includes(candidate.integrationId),\n );\n if (!connection) return c.json({ error: 'connection_not_found' }, 404);\n const session = await client.request<PlatformSessionResponse>(\n 'POST',\n `/v2/connections/${encodeURIComponent(connectionId)}/reconnect-session`,\n {},\n );\n c.header('Cache-Control', 'no-store');\n return c.json(session, 201);\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n ];\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/github/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKxD,OAAO,KAAK,EAEV,MAAM,EAIP,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/github/integration.ts"],"names":[],"mappings":"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"}
|