@mastra/factory 0.24.0-alpha.1 → 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/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/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.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.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.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 +13 -13
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-control.js","names":[],"sources":["../../../src/integrations/gitlab/version-control.ts"],"sourcesContent":["import type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n PullRequest,\n PullRequestComment,\n RequestedReviewers,\n Review,\n ReviewComment,\n VersionControl,\n} from '../../capabilities/version-control.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport {\n GITLAB_DISCUSSIONS_PAGE_SIZE,\n GITLAB_MERGE_REQUESTS_PAGE_SIZE,\n GITLAB_NOTES_PAGE_SIZE,\n GitLabApiError,\n} from './api.js';\nimport type {\n GitLabApiClient,\n GitLabDiscussionNote,\n GitLabDiscussionPosition,\n GitLabMember,\n GitLabMergeRequest,\n GitLabNote,\n GitLabUser,\n} from './api.js';\n\nexport interface GitLabVersionControlContext {\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n webBaseUrl?: string;\n repositoryAccessToken?: () => Promise<string>;\n}\n\nexport interface GitLabVersionControlDependencies {\n contextForConnection(connection: IntegrationConnection): Promise<GitLabVersionControlContext>;\n contextForStoredInstallation?(\n connection: IntegrationConnection,\n host: string | undefined,\n ): Promise<GitLabVersionControlContext>;\n}\n\nexport function buildGitLabVersionControl(deps: GitLabVersionControlDependencies): VersionControl {\n let storage: SourceControlStorageHandle | undefined;\n\n const sourceControlStorage = (): SourceControlStorageHandle => {\n if (!storage) throw new Error('GitLab VersionControl is not initialized.');\n return storage;\n };\n\n const listPullRequests: VersionControl['listPullRequests'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const page = parsePositiveCursor(input.cursor);\n const state = input.state === 'open' ? 'opened' : input.state === 'closed' ? 'all' : (input.state ?? 'opened');\n const mergeRequests = await context.api.listMergeRequests(input.sourceId, { page, state });\n return {\n pullRequests: mergeRequests\n .filter(\n mergeRequest =>\n input.state !== 'closed' || mergeRequest.state === 'closed' || mergeRequest.state === 'merged',\n )\n .filter(mergeRequest => input.includeDrafts !== false || !isDraft(mergeRequest))\n .map(toPullRequest),\n nextCursor: mergeRequests.length === GITLAB_MERGE_REQUESTS_PAGE_SIZE ? String(page + 1) : null,\n };\n };\n\n const getPullRequest: VersionControl['getPullRequest'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n try {\n return toPullRequest(\n await context.api.getMergeRequest(input.sourceId, requirePositiveId(input.pullRequestId, 'merge request')),\n );\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n };\n\n const createPullRequest: VersionControl['createPullRequest'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const title = input.draft && !/^(?:draft:|\\[draft\\])/i.test(input.title) ? `Draft: ${input.title}` : input.title;\n return toPullRequest(\n await context.api.createMergeRequest(input.sourceId, {\n sourceBranch: input.headBranch,\n targetBranch: input.baseBranch,\n title,\n description: input.body,\n }),\n );\n };\n\n const updatePullRequest: VersionControl['updatePullRequest'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n return toPullRequest(\n await context.api.updateMergeRequest(input.sourceId, requirePositiveId(input.pullRequestId, 'merge request'), {\n title: input.title,\n description: input.body === null ? '' : input.body,\n targetBranch: input.baseBranch,\n stateEvent: input.state === 'closed' ? 'close' : input.state === 'open' ? 'reopen' : undefined,\n }),\n );\n };\n\n const closePullRequest: VersionControl['closePullRequest'] = input =>\n updatePullRequest({ ...input, state: 'closed' });\n\n const mergePullRequest: VersionControl['mergePullRequest'] = async input => {\n if (input.method === 'rebase') {\n throw notSupported('GitLab rebases merge requests asynchronously; rebase-and-merge is not supported.');\n }\n const context = await deps.contextForConnection(input.connection);\n const commitMessage = combinedCommitMessage(input.commitTitle, input.commitMessage);\n const result = await context.api.mergeMergeRequest(\n input.sourceId,\n requirePositiveId(input.pullRequestId, 'merge request'),\n {\n squash: input.method === 'squash',\n mergeCommitMessage: input.method === 'squash' ? undefined : commitMessage,\n squashCommitMessage: input.method === 'squash' ? commitMessage : undefined,\n },\n );\n const merged = result.state === 'merged' || Boolean(result.merged_at);\n return {\n merged,\n message: result.message ?? (merged ? 'Merge request merged.' : 'Merge request was not merged.'),\n sha: result.squash_commit_sha ?? result.merge_commit_sha ?? result.sha ?? null,\n };\n };\n\n const listComments: VersionControl['listComments'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const page = parsePositiveCursor(input.cursor);\n const notes = await context.api.listMergeRequestNotes(input.sourceId, mergeRequestIid, { page });\n return {\n comments: notes\n .filter(note => !note.system && note.type !== 'DiffNote')\n .map(note => toPullRequestComment(webBaseUrl(context), input.sourceId, mergeRequestIid, note)),\n nextCursor: notes.length === GITLAB_NOTES_PAGE_SIZE ? String(page + 1) : null,\n };\n };\n\n const createComment: VersionControl['createComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const note = await context.api.createMergeRequestNote(input.sourceId, mergeRequestIid, input.body);\n return toPullRequestComment(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n };\n\n const updateComment: VersionControl['updateComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const { mergeRequestIid, noteId } = parseNoteId(input.commentId);\n const note = await context.api.updateMergeRequestNote(input.sourceId, mergeRequestIid, noteId, input.body);\n return toPullRequestComment(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n };\n\n const deleteComment: VersionControl['deleteComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const { mergeRequestIid, noteId } = parseNoteId(input.commentId);\n await context.api.deleteMergeRequestNote(input.sourceId, mergeRequestIid, noteId);\n };\n\n const listReviews: VersionControl['listReviews'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const approvals = await context.api.getMergeRequestApprovals(input.sourceId, mergeRequestIid);\n return {\n reviews: (approvals.approved_by ?? []).map(({ user }) =>\n toApprovalReview(webBaseUrl(context), input.sourceId, mergeRequestIid, user),\n ),\n nextCursor: null,\n };\n };\n\n const getReview: VersionControl['getReview'] = async input => {\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const approvalPrefix = `${mergeRequestIid}:approval`;\n const commentPrefix = `${mergeRequestIid}:comment:`;\n if (\n input.reviewId !== approvalPrefix &&\n !input.reviewId.startsWith(`${approvalPrefix}:`) &&\n !input.reviewId.startsWith(commentPrefix)\n ) {\n return null;\n }\n const context = await deps.contextForConnection(input.connection);\n if (input.reviewId === approvalPrefix || input.reviewId.startsWith(`${approvalPrefix}:`)) {\n const reviewerKey = input.reviewId === approvalPrefix ? null : input.reviewId.slice(approvalPrefix.length + 1);\n if (reviewerKey === '') return null;\n const currentUser = reviewerKey === null ? await context.api.getCurrentUser() : null;\n const approvals = await context.api.getMergeRequestApprovals(input.sourceId, mergeRequestIid);\n const user = (approvals.approved_by ?? [])\n .map(entry => entry.user)\n .find(candidate =>\n reviewerKey === null\n ? (currentUser?.id !== undefined && candidate.id === currentUser.id) ||\n candidate.username === currentUser?.username\n : String(candidate.id ?? candidate.username) === reviewerKey,\n );\n return user ? toApprovalReview(webBaseUrl(context), input.sourceId, mergeRequestIid, user) : null;\n }\n const noteId = parsePositiveInteger(input.reviewId.slice(commentPrefix.length));\n if (noteId === null) return null;\n try {\n const note = await context.api.getMergeRequestNote(input.sourceId, mergeRequestIid, noteId);\n if (note.system || note.type === 'DiffNote') return null;\n return toCommentReview(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n };\n\n const createReview: VersionControl['createReview'] = async input =>\n submitReviewAction(deps, {\n connection: input.connection,\n sourceId: input.sourceId,\n pullRequestId: input.pullRequestId,\n event: input.event,\n body: input.body,\n commitId: input.commitId,\n });\n\n const updateReview: VersionControl['updateReview'] = async () => {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab does not expose mutable pending review objects.');\n };\n\n const submitReview: VersionControl['submitReview'] = async input =>\n submitReviewAction(deps, {\n connection: input.connection,\n sourceId: input.sourceId,\n pullRequestId: input.pullRequestId,\n event: input.event,\n body: input.body,\n });\n\n const dismissReview: VersionControl['dismissReview'] = async () => {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab approval dismissal has no GitHub review equivalent.');\n };\n\n const deletePendingReview: VersionControl['deletePendingReview'] = async () => {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab does not expose pending review objects.');\n };\n\n const listReviewComments: VersionControl['listReviewComments'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const page = parsePositiveCursor(input.cursor);\n const discussions = await context.api.listMergeRequestDiscussions(input.sourceId, mergeRequestIid, { page });\n return {\n comments: discussions.flatMap(discussion => {\n const position = discussionPosition(discussion.notes);\n if (!position) return [];\n const root = discussion.notes.find(note => note.position) ?? discussion.notes[0];\n const replyToId = root ? packDiscussionNoteId(mergeRequestIid, discussion.id, root.id) : null;\n return discussion.notes\n .filter(note => !note.system)\n .map(note =>\n toReviewComment(\n webBaseUrl(context),\n input.sourceId,\n mergeRequestIid,\n discussion.id,\n note,\n note === root ? null : replyToId,\n position,\n ),\n );\n }),\n nextCursor: discussions.length === GITLAB_DISCUSSIONS_PAGE_SIZE ? String(page + 1) : null,\n };\n };\n\n const createReviewComment: VersionControl['createReviewComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n if (input.replyToId) {\n const thread = parseReviewReplyId(input.replyToId);\n if (thread.mergeRequestIid !== mergeRequestIid) {\n throw new GitLabApiError('GitLab discussion does not belong to the requested merge request.', 400);\n }\n const discussion = await context.api.getMergeRequestDiscussion(\n input.sourceId,\n mergeRequestIid,\n thread.discussionId,\n );\n const position = discussionPosition(discussion.notes);\n if (!position) throw new GitLabApiError('GitLab discussion is not anchored to a diff.', 400);\n const root = discussion.notes.find(note => note.position) ?? discussion.notes[0];\n if (!root) throw new GitLabApiError('GitLab discussion response did not include a note.', 502);\n const note = await context.api.addMergeRequestDiscussionNote(\n input.sourceId,\n mergeRequestIid,\n thread.discussionId,\n input.body,\n );\n return toReviewComment(\n webBaseUrl(context),\n input.sourceId,\n mergeRequestIid,\n thread.discussionId,\n note,\n packDiscussionNoteId(mergeRequestIid, thread.discussionId, root.id),\n position,\n );\n }\n\n const mergeRequest = await context.api.getMergeRequest(input.sourceId, mergeRequestIid);\n if (!mergeRequest.diff_refs) {\n throw new GitLabApiError('GitLab merge request diff refs are not ready.', 409);\n }\n if (mergeRequest.diff_refs.head_sha !== input.commitId) {\n throw new GitLabApiError('GitLab merge request changed since this review comment was prepared.', 409);\n }\n const { path, line, side } = input;\n if (typeof path !== 'string' || typeof line !== 'number' || (side !== 'left' && side !== 'right')) {\n throw new GitLabApiError('GitLab diff review comment position is invalid.', 400);\n }\n if ((input.startLine === undefined) !== (input.startSide === undefined)) {\n throw new GitLabApiError('A multi-line GitLab review comment requires both startLine and startSide.', 400);\n }\n const position: GitLabDiscussionPosition = {\n position_type: 'text',\n ...mergeRequest.diff_refs,\n old_path: path,\n new_path: path,\n old_line: side === 'left' ? line : undefined,\n new_line: side === 'right' ? line : undefined,\n ...(input.startLine !== undefined && input.startSide\n ? {\n line_range: {\n start: await discussionLine(path, input.startLine, input.startSide),\n end: await discussionLine(path, line, side),\n },\n }\n : {}),\n };\n const discussion = await context.api.createMergeRequestDiscussion(input.sourceId, mergeRequestIid, {\n body: input.body,\n commitId: input.commitId,\n position,\n });\n const note = discussion.notes.find(candidate => candidate.position) ?? discussion.notes[0];\n if (!note) throw new GitLabApiError('GitLab discussion response did not include a note.', 502);\n return toReviewComment(webBaseUrl(context), input.sourceId, mergeRequestIid, discussion.id, note, null, position);\n };\n\n const updateReviewComment: VersionControl['updateReviewComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const reference = parseDiscussionNoteId(input.commentId);\n const discussion = await context.api.getMergeRequestDiscussion(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n );\n const position = discussionPosition(discussion.notes);\n if (!position) throw new GitLabApiError('GitLab discussion is not anchored to a diff.', 400);\n const root = discussion.notes.find(candidate => candidate.position) ?? discussion.notes[0];\n if (!root) throw new GitLabApiError('GitLab discussion response did not include a note.', 502);\n const note = await context.api.updateMergeRequestDiscussionNote(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n reference.noteId,\n input.body,\n );\n return toReviewComment(\n webBaseUrl(context),\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n note,\n note.id === root.id ? null : packDiscussionNoteId(reference.mergeRequestIid, reference.discussionId, root.id),\n position,\n );\n };\n\n const deleteReviewComment: VersionControl['deleteReviewComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const reference = parseDiscussionNoteId(input.commentId);\n await context.api.deleteMergeRequestDiscussionNote(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n reference.noteId,\n );\n };\n\n const resolveReviewThread: NonNullable<VersionControl['resolveReviewThread']> = async input => {\n const context = await deps.contextForConnection(input.connection);\n const reference = parseDiscussionNoteId(input.commentId);\n await context.api.resolveMergeRequestDiscussion(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n input.resolved,\n );\n };\n\n const listRequestedReviewers: VersionControl['listRequestedReviewers'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequest = await context.api.getMergeRequest(\n input.sourceId,\n requirePositiveId(input.pullRequestId, 'merge request'),\n );\n return toRequestedReviewers(mergeRequest);\n };\n\n const requestReviewers: VersionControl['requestReviewers'] = async input => {\n rejectTeamReviewers(input.teams);\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const mergeRequest = await context.api.getMergeRequest(input.sourceId, mergeRequestIid);\n const requestedMembers = await resolveMembers(context.api, input.sourceId, input.users ?? []);\n const currentIds = await reviewerIds(context.api, input.sourceId, mergeRequest.reviewers ?? []);\n const reviewerIdsToSet = [...new Set([...currentIds, ...requestedMembers.map(member => member.id)])];\n return toRequestedReviewers(\n await context.api.setMergeRequestReviewers(input.sourceId, mergeRequestIid, reviewerIdsToSet),\n );\n };\n\n const removeRequestedReviewers: VersionControl['removeRequestedReviewers'] = async input => {\n rejectTeamReviewers(input.teams);\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const mergeRequest = await context.api.getMergeRequest(input.sourceId, mergeRequestIid);\n const removals = new Set((input.users ?? []).map(username => username.toLowerCase()));\n const retained = (mergeRequest.reviewers ?? []).filter(user => !removals.has(user.username.toLowerCase()));\n const retainedIds = await reviewerIds(context.api, input.sourceId, retained);\n return toRequestedReviewers(\n await context.api.setMergeRequestReviewers(input.sourceId, mergeRequestIid, retainedIds),\n );\n };\n\n return {\n initialize: input => {\n storage = input.storage;\n },\n registerInstallation: async ({ orgId, userId, installation }) => {\n const requestedConnection = parseConnection(installation.metadata?.connection);\n if (!requestedConnection) {\n throw new GitLabApiError('GitLab installation metadata must include a valid connection.', 400);\n }\n const context = await deps.contextForConnection(requestedConnection);\n return sourceControlStorage().installations.upsert({\n orgId,\n connectedByUserId: userId,\n externalId: installation.externalId,\n accountName: installation.accountName,\n accountType: installation.accountType,\n providerMetadata: {\n ...installation.metadata,\n connection: context.connection,\n host: normalizeHost(context.host),\n },\n });\n },\n registerRepositories: async ({ orgId, installationId, repositories }) => {\n const normalizedRepositories = repositories.map(repository => ({\n ...repository,\n slug: normalizeSlug(repository.slug),\n }));\n return await Promise.all(\n normalizedRepositories.map(repository =>\n sourceControlStorage().repositories.upsert({\n orgId,\n input: {\n installationId,\n externalId: repository.externalId,\n slug: repository.slug,\n defaultBranch: repository.defaultBranch,\n providerMetadata: repository.metadata,\n },\n }),\n ),\n );\n },\n getRepositoryTarget: async ({ orgId, repositoryId }) => {\n const repository = await sourceControlStorage().repositories.get({ orgId, id: repositoryId });\n if (!repository) throw new Error('Version-control repository not found.');\n const installation = await sourceControlStorage().installations.get({\n orgId,\n id: repository.installationId,\n });\n if (!installation) throw new Error('Version-control installation not found.');\n const connection = parseConnection(installation.providerMetadata.connection);\n if (!connection) throw new GitLabApiError('GitLab installation connection metadata is invalid.', 500);\n const context = await (deps.contextForStoredInstallation ?? deps.contextForConnection)(\n connection,\n typeof installation.providerMetadata.host === 'string' ? installation.providerMetadata.host : undefined,\n );\n // The API uses GitLab's immutable numeric project ID because Platform's\n // proxy normalizes encoded path slashes; retain the slug for browser URLs.\n return { connection: context.connection, sourceId: `${repository.externalId}:${repository.slug}` };\n },\n getRepositoryAccess: async ({ orgId, repositoryId }) => {\n const repository = await sourceControlStorage().repositories.get({ orgId, id: repositoryId });\n if (!repository) throw new Error('Version-control repository not found.');\n const installation = await sourceControlStorage().installations.get({\n orgId,\n id: repository.installationId,\n });\n if (!installation) throw new Error('Version-control installation not found.');\n const connection = parseConnection(installation.providerMetadata.connection);\n if (!connection) throw new GitLabApiError('GitLab installation connection metadata is invalid.', 500);\n const context = await (deps.contextForStoredInstallation ?? deps.contextForConnection)(\n connection,\n typeof installation.providerMetadata.host === 'string' ? installation.providerMetadata.host : undefined,\n );\n if (!context.repositoryAccessToken) {\n throw notSupported('This GitLab connection does not expose credentials for repository cloning.');\n }\n const token = await context.repositoryAccessToken();\n const baseUrl = webBaseUrl(context);\n const slug = normalizeSlug(repository.slug);\n return {\n cloneUrl: `${baseUrl}/${slug}.git`,\n authorization: { scheme: 'bearer', token, username: 'oauth2' },\n };\n },\n listPullRequests,\n getPullRequest,\n createPullRequest,\n updatePullRequest,\n closePullRequest,\n mergePullRequest,\n listComments,\n resolveReviewThread,\n createComment,\n updateComment,\n deleteComment,\n listReviews,\n getReview,\n createReview,\n updateReview,\n submitReview,\n dismissReview,\n deletePendingReview,\n listReviewComments,\n createReviewComment,\n updateReviewComment,\n deleteReviewComment,\n listRequestedReviewers,\n requestReviewers,\n removeRequestedReviewers,\n };\n}\n\nasync function submitReviewAction(\n deps: GitLabVersionControlDependencies,\n input: {\n connection: IntegrationConnection;\n sourceId: string;\n pullRequestId: string;\n event: 'approve' | 'request-changes' | 'comment' | undefined;\n body?: string;\n commitId?: string;\n },\n): Promise<Review> {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n if (input.event === 'request-changes') {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab has no first-class request-changes review event.');\n }\n if (input.event === undefined) {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab has no first-class pending review object.');\n }\n if (input.event === 'approve') {\n const body = input.body?.trim();\n await context.api.approveMergeRequest(input.sourceId, mergeRequestIid, input.commitId);\n const comment = body\n ? toPullRequestComment(\n webBaseUrl(context),\n input.sourceId,\n mergeRequestIid,\n await context.api.createMergeRequestNote(input.sourceId, mergeRequestIid, body),\n )\n : null;\n return {\n id: String(mergeRequestIid) + ':approval',\n url: comment?.url ?? mergeRequestUrl(webBaseUrl(context), input.sourceId, mergeRequestIid),\n author: comment?.author ?? null,\n body: comment?.body ?? null,\n state: 'approved',\n commitId: null,\n submittedAt: comment?.createdAt ?? null,\n };\n }\n const body = input.body?.trim();\n if (!body) throw new GitLabApiError('GitLab comment reviews require a body.', 400);\n const note = await context.api.createMergeRequestNote(input.sourceId, mergeRequestIid, body);\n return toCommentReview(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n}\n\nfunction toCommentReview(host: string, sourceId: string, mergeRequestIid: number, note: GitLabNote): Review {\n const comment = toPullRequestComment(host, sourceId, mergeRequestIid, note);\n return {\n id: `${mergeRequestIid}:comment:${note.id}`,\n url: comment.url,\n author: comment.author,\n body: comment.body,\n state: 'commented',\n commitId: null,\n submittedAt: note.created_at,\n };\n}\n\nfunction toApprovalReview(host: string, sourceId: string, mergeRequestIid: number, user: GitLabUser): Review {\n return {\n id: `${mergeRequestIid}:approval:${user.id ?? user.username}`,\n url: mergeRequestUrl(host, sourceId, mergeRequestIid),\n author: displayName(user),\n body: null,\n state: 'approved',\n commitId: null,\n submittedAt: null,\n };\n}\n\nfunction toReviewComment(\n host: string,\n sourceId: string,\n mergeRequestIid: number,\n discussionId: string,\n note: GitLabDiscussionNote,\n replyToId: string | null,\n fallbackPosition?: GitLabDiscussionPosition,\n): ReviewComment {\n const position = note.position ?? fallbackPosition;\n if (!position) throw new GitLabApiError('GitLab review comment is missing its diff position.', 502);\n const base = toPullRequestComment(host, sourceId, mergeRequestIid, note);\n const side = position.new_line !== null && position.new_line !== undefined ? 'right' : 'left';\n return {\n ...base,\n id: packDiscussionNoteId(mergeRequestIid, discussionId, note.id),\n path: side === 'right' ? position.new_path : position.old_path,\n line: side === 'right' ? (position.new_line ?? null) : (position.old_line ?? null),\n side,\n commitId: position.head_sha,\n replyToId,\n };\n}\n\nfunction mergeRequestUrl(host: string, sourceId: string, mergeRequestIid: number): string {\n return `${host}/${sourcePath(sourceId)}/-/merge_requests/${mergeRequestIid}`;\n}\n\nfunction webBaseUrl(context: GitLabVersionControlContext): string {\n const host = normalizeHost(context.host);\n if (!context.webBaseUrl) return `https://${host}`;\n let url: URL;\n try {\n url = new URL(context.webBaseUrl);\n } catch {\n throw new GitLabApiError('GitLab web base URL is invalid.', 400);\n }\n const loopback =\n url.hostname === 'localhost' || url.hostname === '[::1]' || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname);\n if (\n url.host !== host ||\n (url.protocol !== 'https:' && !(url.protocol === 'http:' && loopback)) ||\n url.username ||\n url.password ||\n url.search ||\n url.hash\n ) {\n throw new GitLabApiError('GitLab web base URL is invalid.', 400);\n }\n return `${url.origin}${url.pathname.replace(/\\/+$/, '')}`;\n}\n\nfunction sourcePath(sourceId: string): string {\n const match = /^\\d+:(.+)$/.exec(sourceId);\n return normalizeSlug(match?.[1] ?? sourceId);\n}\n\nfunction packDiscussionId(mergeRequestIid: number, discussionId: string): string {\n if (!discussionId || discussionId.includes(':')) {\n throw new GitLabApiError('GitLab discussion id is invalid.', 400);\n }\n return `${mergeRequestIid}:${discussionId}`;\n}\n\nfunction packDiscussionNoteId(mergeRequestIid: number, discussionId: string, noteId: number): string {\n return `${packDiscussionId(mergeRequestIid, discussionId)}:${noteId}`;\n}\n\nfunction parseReviewReplyId(value: string): { mergeRequestIid: number; discussionId: string } {\n const parts = value.split(':');\n if (parts.length === 2) return parseDiscussionId(value);\n if (parts.length === 3 && parts[1]) {\n requirePositiveId(parts[2]!, 'discussion note');\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n discussionId: parts[1],\n };\n }\n throw new GitLabApiError('GitLab review reply id is invalid.', 400);\n}\n\nfunction parseDiscussionId(value: string): { mergeRequestIid: number; discussionId: string } {\n const parts = value.split(':');\n if (parts.length !== 2 || !parts[1]) throw new GitLabApiError('GitLab discussion id is invalid.', 400);\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n discussionId: parts[1],\n };\n}\n\nfunction parseDiscussionNoteId(value: string): { mergeRequestIid: number; discussionId: string; noteId: number } {\n const parts = value.split(':');\n if (parts.length !== 3 || !parts[1]) throw new GitLabApiError('GitLab discussion note id is invalid.', 400);\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n discussionId: parts[1],\n noteId: requirePositiveId(parts[2]!, 'discussion note'),\n };\n}\n\nfunction discussionPosition(notes: GitLabDiscussionNote[]): GitLabDiscussionPosition | undefined {\n return notes.find(note => note.position)?.position ?? undefined;\n}\n\nasync function discussionLine(path: string, line: number, side: 'left' | 'right') {\n const pathHash = Buffer.from(await globalThis.crypto.subtle.digest('SHA-1', new TextEncoder().encode(path))).toString(\n 'hex',\n );\n const oldLine = side === 'left' ? line : undefined;\n const newLine = side === 'right' ? line : undefined;\n return {\n line_code: `${pathHash}_${oldLine ?? 0}_${newLine ?? 0}`,\n type: side === 'left' ? ('old' as const) : ('new' as const),\n old_line: oldLine,\n new_line: newLine,\n };\n}\n\nfunction toRequestedReviewers(mergeRequest: GitLabMergeRequest): RequestedReviewers {\n return {\n users: (mergeRequest.reviewers ?? []).map(user => user.username),\n teams: [],\n };\n}\n\nasync function resolveMembers(api: GitLabApiClient, sourceId: string, usernames: string[]): Promise<GitLabMember[]> {\n return Promise.all(usernames.map(username => resolveMember(api, sourceId, username)));\n}\n\nasync function resolveMember(api: GitLabApiClient, sourceId: string, username: string): Promise<GitLabMember> {\n const normalized = username.trim();\n if (!normalized) throw new GitLabApiError('GitLab reviewer username is invalid.', 400);\n const members = await api.listProjectMembers(sourceId, { query: normalized });\n const member = members.find(candidate => candidate.username.toLowerCase() === normalized.toLowerCase());\n if (!member) throw new GitLabApiError(`GitLab reviewer ${normalized} is not a project member.`, 404);\n return member;\n}\n\nasync function reviewerIds(api: GitLabApiClient, sourceId: string, users: GitLabUser[]): Promise<number[]> {\n return Promise.all(users.map(async user => user.id ?? (await resolveMember(api, sourceId, user.username)).id));\n}\n\nfunction rejectTeamReviewers(teams: string[] | undefined): void {\n if (!teams?.length) return;\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab merge requests do not have a direct team-reviewer equivalent.');\n}\n\nfunction notSupported(message: string): GitLabApiError {\n return new GitLabApiError(message, 501);\n}\n\nfunction toPullRequest(mergeRequest: GitLabMergeRequest): PullRequest {\n return {\n id: String(mergeRequest.iid),\n title: mergeRequest.title,\n url: mergeRequest.web_url,\n author: displayName(mergeRequest.author),\n assignees: mergeRequest.assignees?.map(user => user.username),\n requestedReviewers: mergeRequest.reviewers?.map(user => user.username),\n labels: mergeRequest.labels,\n body: mergeRequest.description?.trim() ? mergeRequest.description : null,\n state: mergeRequest.state === 'closed' || mergeRequest.state === 'merged' ? 'closed' : 'open',\n draft: isDraft(mergeRequest),\n merged: mergeRequest.state === 'merged' || Boolean(mergeRequest.merged_at),\n mergeable: mergeableState(mergeRequest.merge_status),\n baseBranch: mergeRequest.target_branch,\n headBranch: mergeRequest.source_branch,\n headSha: mergeRequest.sha,\n createdAt: mergeRequest.created_at,\n updatedAt: mergeRequest.updated_at,\n };\n}\n\nfunction toPullRequestComment(\n host: string,\n sourceId: string,\n mergeRequestIid: number,\n note: GitLabNote,\n): PullRequestComment {\n return {\n id: `${mergeRequestIid}:${note.id}`,\n url: `${mergeRequestUrl(host, sourceId, mergeRequestIid)}#note_${note.id}`,\n author: displayName(note.author),\n body: note.body,\n createdAt: note.created_at,\n updatedAt: note.updated_at ?? note.created_at,\n };\n}\n\nfunction displayName(user: { name?: string | null; username: string } | null | undefined): string | null {\n return user?.username || user?.name?.trim() || null;\n}\n\nfunction isDraft(mergeRequest: GitLabMergeRequest): boolean {\n return mergeRequest.draft ?? mergeRequest.work_in_progress ?? /^(?:draft:|\\[draft\\])/i.test(mergeRequest.title);\n}\n\nfunction mergeableState(status: string | undefined): boolean | null {\n if (status === 'can_be_merged') return true;\n if (status === 'cannot_be_merged') return false;\n return null;\n}\n\nfunction combinedCommitMessage(title: string | undefined, body: string | undefined): string | undefined {\n const parts = [title?.trim(), body?.trim()].filter((part): part is string => Boolean(part));\n return parts.length > 0 ? parts.join('\\n\\n') : undefined;\n}\n\nfunction parseNoteId(value: string): { mergeRequestIid: number; noteId: number } {\n const parts = value.split(':');\n if (parts.length !== 2) throw new GitLabApiError('GitLab merge request note id is invalid.', 400);\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n noteId: requirePositiveId(parts[1]!, 'note'),\n };\n}\n\nfunction parsePositiveCursor(cursor: string | undefined): number {\n if (cursor === undefined) return 1;\n const page = parsePositiveInteger(cursor);\n if (page === null) throw new GitLabApiError('GitLab cursor must be a positive page number.', 400);\n return page;\n}\n\nfunction requirePositiveId(value: string, resource: string): number {\n const parsed = parsePositiveInteger(value);\n if (parsed === null) throw new GitLabApiError(`GitLab ${resource} id must be a positive integer.`, 400);\n return parsed;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseConnection(value: unknown): IntegrationConnection | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const connection = value as Record<string, unknown>;\n if (connection.type === 'oauth' && typeof connection.accessToken === 'string' && connection.accessToken.length > 0) {\n return { type: 'oauth', accessToken: connection.accessToken };\n }\n if (\n connection.type === 'app-installation' &&\n Number.isSafeInteger(connection.installationId) &&\n Number(connection.installationId) > 0\n ) {\n return { type: 'app-installation', installationId: Number(connection.installationId) };\n }\n return null;\n}\n\nfunction normalizeHost(value: string): string {\n const host = value.trim();\n if (!host || host.includes('/') || host.includes('@')) throw new GitLabApiError('GitLab host is invalid.', 400);\n let parsed: URL;\n try {\n parsed = new URL(`https://${host}`);\n } catch {\n throw new GitLabApiError('GitLab host is invalid.', 400);\n }\n if (parsed.host !== host) throw new GitLabApiError('GitLab host is invalid.', 400);\n return host;\n}\n\nfunction normalizeSlug(value: string): string {\n const slug = value.replace(/^\\/+|\\/+$/g, '');\n const segments = slug.split('/');\n if (!slug || segments.some(segment => !/^[A-Za-z0-9_.-]+$/.test(segment) || segment === '.' || segment === '..')) {\n throw new GitLabApiError('GitLab repository slug is invalid.', 400);\n }\n return slug;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"version-control.js","names":[],"sources":["../../../src/integrations/gitlab/version-control.ts"],"sourcesContent":["import type { IntegrationConnection } from '../../capabilities/connection.js';\nimport type {\n CommitCheckStatus,\n PullRequest,\n PullRequestComment,\n RequestedReviewers,\n Review,\n ReviewComment,\n VersionControl,\n} from '../../capabilities/version-control.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport {\n GITLAB_DISCUSSIONS_PAGE_SIZE,\n GITLAB_MERGE_REQUESTS_PAGE_SIZE,\n GITLAB_NOTES_PAGE_SIZE,\n GitLabApiError,\n} from './api.js';\nimport type {\n GitLabApiClient,\n GitLabCommitStatusState,\n GitLabDiscussionNote,\n GitLabDiscussionPosition,\n GitLabMember,\n GitLabMergeRequest,\n GitLabNote,\n GitLabUser,\n} from './api.js';\n\nexport interface GitLabVersionControlContext {\n api: GitLabApiClient;\n connection: IntegrationConnection;\n host: string;\n webBaseUrl?: string;\n repositoryAccessToken?: () => Promise<string>;\n}\n\nexport interface GitLabVersionControlDependencies {\n contextForConnection(connection: IntegrationConnection): Promise<GitLabVersionControlContext>;\n contextForStoredInstallation?(\n connection: IntegrationConnection,\n host: string | undefined,\n ): Promise<GitLabVersionControlContext>;\n}\n\nexport function buildGitLabVersionControl(deps: GitLabVersionControlDependencies): VersionControl {\n let storage: SourceControlStorageHandle | undefined;\n\n const sourceControlStorage = (): SourceControlStorageHandle => {\n if (!storage) throw new Error('GitLab VersionControl is not initialized.');\n return storage;\n };\n\n const listPullRequests: VersionControl['listPullRequests'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const page = parsePositiveCursor(input.cursor);\n const state = input.state === 'open' ? 'opened' : input.state === 'closed' ? 'all' : (input.state ?? 'opened');\n const mergeRequests = await context.api.listMergeRequests(input.sourceId, { page, state });\n return {\n pullRequests: mergeRequests\n .filter(\n mergeRequest =>\n input.state !== 'closed' || mergeRequest.state === 'closed' || mergeRequest.state === 'merged',\n )\n .filter(mergeRequest => input.includeDrafts !== false || !isDraft(mergeRequest))\n .map(toPullRequest),\n nextCursor: mergeRequests.length === GITLAB_MERGE_REQUESTS_PAGE_SIZE ? String(page + 1) : null,\n };\n };\n\n const getPullRequest: VersionControl['getPullRequest'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n try {\n return toPullRequest(\n await context.api.getMergeRequest(input.sourceId, requirePositiveId(input.pullRequestId, 'merge request')),\n );\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n };\n\n const createPullRequest: VersionControl['createPullRequest'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const title = input.draft && !/^(?:draft:|\\[draft\\])/i.test(input.title) ? `Draft: ${input.title}` : input.title;\n return toPullRequest(\n await context.api.createMergeRequest(input.sourceId, {\n sourceBranch: input.headBranch,\n targetBranch: input.baseBranch,\n title,\n description: input.body,\n }),\n );\n };\n\n const updatePullRequest: VersionControl['updatePullRequest'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n return toPullRequest(\n await context.api.updateMergeRequest(input.sourceId, requirePositiveId(input.pullRequestId, 'merge request'), {\n title: input.title,\n description: input.body === null ? '' : input.body,\n targetBranch: input.baseBranch,\n stateEvent: input.state === 'closed' ? 'close' : input.state === 'open' ? 'reopen' : undefined,\n }),\n );\n };\n\n const closePullRequest: VersionControl['closePullRequest'] = input =>\n updatePullRequest({ ...input, state: 'closed' });\n\n const mergePullRequest: VersionControl['mergePullRequest'] = async input => {\n if (input.method === 'rebase') {\n throw notSupported('GitLab rebases merge requests asynchronously; rebase-and-merge is not supported.');\n }\n const context = await deps.contextForConnection(input.connection);\n const commitMessage = combinedCommitMessage(input.commitTitle, input.commitMessage);\n const result = await context.api.mergeMergeRequest(\n input.sourceId,\n requirePositiveId(input.pullRequestId, 'merge request'),\n {\n squash: input.method === 'squash',\n mergeCommitMessage: input.method === 'squash' ? undefined : commitMessage,\n squashCommitMessage: input.method === 'squash' ? commitMessage : undefined,\n },\n );\n const merged = result.state === 'merged' || Boolean(result.merged_at);\n return {\n merged,\n message: result.message ?? (merged ? 'Merge request merged.' : 'Merge request was not merged.'),\n sha: result.squash_commit_sha ?? result.merge_commit_sha ?? result.sha ?? null,\n };\n };\n\n const listComments: VersionControl['listComments'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const page = parsePositiveCursor(input.cursor);\n const notes = await context.api.listMergeRequestNotes(input.sourceId, mergeRequestIid, { page });\n return {\n comments: notes\n .filter(note => !note.system && note.type !== 'DiffNote')\n .map(note => toPullRequestComment(webBaseUrl(context), input.sourceId, mergeRequestIid, note)),\n nextCursor: notes.length === GITLAB_NOTES_PAGE_SIZE ? String(page + 1) : null,\n };\n };\n\n const createComment: VersionControl['createComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const note = await context.api.createMergeRequestNote(input.sourceId, mergeRequestIid, input.body);\n return toPullRequestComment(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n };\n\n const updateComment: VersionControl['updateComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const { mergeRequestIid, noteId } = parseNoteId(input.commentId);\n const note = await context.api.updateMergeRequestNote(input.sourceId, mergeRequestIid, noteId, input.body);\n return toPullRequestComment(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n };\n\n const deleteComment: VersionControl['deleteComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const { mergeRequestIid, noteId } = parseNoteId(input.commentId);\n await context.api.deleteMergeRequestNote(input.sourceId, mergeRequestIid, noteId);\n };\n\n const listReviews: VersionControl['listReviews'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const approvals = await context.api.getMergeRequestApprovals(input.sourceId, mergeRequestIid);\n return {\n reviews: (approvals.approved_by ?? []).map(({ user }) =>\n toApprovalReview(webBaseUrl(context), input.sourceId, mergeRequestIid, user),\n ),\n nextCursor: null,\n };\n };\n\n const getReview: VersionControl['getReview'] = async input => {\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const approvalPrefix = `${mergeRequestIid}:approval`;\n const commentPrefix = `${mergeRequestIid}:comment:`;\n if (\n input.reviewId !== approvalPrefix &&\n !input.reviewId.startsWith(`${approvalPrefix}:`) &&\n !input.reviewId.startsWith(commentPrefix)\n ) {\n return null;\n }\n const context = await deps.contextForConnection(input.connection);\n if (input.reviewId === approvalPrefix || input.reviewId.startsWith(`${approvalPrefix}:`)) {\n const reviewerKey = input.reviewId === approvalPrefix ? null : input.reviewId.slice(approvalPrefix.length + 1);\n if (reviewerKey === '') return null;\n const currentUser = reviewerKey === null ? await context.api.getCurrentUser() : null;\n const approvals = await context.api.getMergeRequestApprovals(input.sourceId, mergeRequestIid);\n const user = (approvals.approved_by ?? [])\n .map(entry => entry.user)\n .find(candidate =>\n reviewerKey === null\n ? (currentUser?.id !== undefined && candidate.id === currentUser.id) ||\n candidate.username === currentUser?.username\n : String(candidate.id ?? candidate.username) === reviewerKey,\n );\n return user ? toApprovalReview(webBaseUrl(context), input.sourceId, mergeRequestIid, user) : null;\n }\n const noteId = parsePositiveInteger(input.reviewId.slice(commentPrefix.length));\n if (noteId === null) return null;\n try {\n const note = await context.api.getMergeRequestNote(input.sourceId, mergeRequestIid, noteId);\n if (note.system || note.type === 'DiffNote') return null;\n return toCommentReview(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n } catch (error) {\n if (error instanceof GitLabApiError && error.status === 404) return null;\n throw error;\n }\n };\n\n const createReview: VersionControl['createReview'] = async input =>\n submitReviewAction(deps, {\n connection: input.connection,\n sourceId: input.sourceId,\n pullRequestId: input.pullRequestId,\n event: input.event,\n body: input.body,\n commitId: input.commitId,\n });\n\n const updateReview: VersionControl['updateReview'] = async () => {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab does not expose mutable pending review objects.');\n };\n\n const submitReview: VersionControl['submitReview'] = async input =>\n submitReviewAction(deps, {\n connection: input.connection,\n sourceId: input.sourceId,\n pullRequestId: input.pullRequestId,\n event: input.event,\n body: input.body,\n });\n\n const dismissReview: VersionControl['dismissReview'] = async () => {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab approval dismissal has no GitHub review equivalent.');\n };\n\n const deletePendingReview: VersionControl['deletePendingReview'] = async () => {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab does not expose pending review objects.');\n };\n\n const listReviewComments: VersionControl['listReviewComments'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const page = parsePositiveCursor(input.cursor);\n const discussions = await context.api.listMergeRequestDiscussions(input.sourceId, mergeRequestIid, { page });\n return {\n comments: discussions.flatMap(discussion => {\n const position = discussionPosition(discussion.notes);\n if (!position) return [];\n const root = discussion.notes.find(note => note.position) ?? discussion.notes[0];\n const replyToId = root ? packDiscussionNoteId(mergeRequestIid, discussion.id, root.id) : null;\n return discussion.notes\n .filter(note => !note.system)\n .map(note =>\n toReviewComment(\n webBaseUrl(context),\n input.sourceId,\n mergeRequestIid,\n discussion.id,\n note,\n note === root ? null : replyToId,\n position,\n ),\n );\n }),\n nextCursor: discussions.length === GITLAB_DISCUSSIONS_PAGE_SIZE ? String(page + 1) : null,\n };\n };\n\n const createReviewComment: VersionControl['createReviewComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n if (input.replyToId) {\n const thread = parseReviewReplyId(input.replyToId);\n if (thread.mergeRequestIid !== mergeRequestIid) {\n throw new GitLabApiError('GitLab discussion does not belong to the requested merge request.', 400);\n }\n const discussion = await context.api.getMergeRequestDiscussion(\n input.sourceId,\n mergeRequestIid,\n thread.discussionId,\n );\n const position = discussionPosition(discussion.notes);\n if (!position) throw new GitLabApiError('GitLab discussion is not anchored to a diff.', 400);\n const root = discussion.notes.find(note => note.position) ?? discussion.notes[0];\n if (!root) throw new GitLabApiError('GitLab discussion response did not include a note.', 502);\n const note = await context.api.addMergeRequestDiscussionNote(\n input.sourceId,\n mergeRequestIid,\n thread.discussionId,\n input.body,\n );\n return toReviewComment(\n webBaseUrl(context),\n input.sourceId,\n mergeRequestIid,\n thread.discussionId,\n note,\n packDiscussionNoteId(mergeRequestIid, thread.discussionId, root.id),\n position,\n );\n }\n\n const mergeRequest = await context.api.getMergeRequest(input.sourceId, mergeRequestIid);\n if (!mergeRequest.diff_refs) {\n throw new GitLabApiError('GitLab merge request diff refs are not ready.', 409);\n }\n if (mergeRequest.diff_refs.head_sha !== input.commitId) {\n throw new GitLabApiError('GitLab merge request changed since this review comment was prepared.', 409);\n }\n const { path, line, side } = input;\n if (typeof path !== 'string' || typeof line !== 'number' || (side !== 'left' && side !== 'right')) {\n throw new GitLabApiError('GitLab diff review comment position is invalid.', 400);\n }\n if ((input.startLine === undefined) !== (input.startSide === undefined)) {\n throw new GitLabApiError('A multi-line GitLab review comment requires both startLine and startSide.', 400);\n }\n const position: GitLabDiscussionPosition = {\n position_type: 'text',\n ...mergeRequest.diff_refs,\n old_path: path,\n new_path: path,\n old_line: side === 'left' ? line : undefined,\n new_line: side === 'right' ? line : undefined,\n ...(input.startLine !== undefined && input.startSide\n ? {\n line_range: {\n start: await discussionLine(path, input.startLine, input.startSide),\n end: await discussionLine(path, line, side),\n },\n }\n : {}),\n };\n const discussion = await context.api.createMergeRequestDiscussion(input.sourceId, mergeRequestIid, {\n body: input.body,\n commitId: input.commitId,\n position,\n });\n const note = discussion.notes.find(candidate => candidate.position) ?? discussion.notes[0];\n if (!note) throw new GitLabApiError('GitLab discussion response did not include a note.', 502);\n return toReviewComment(webBaseUrl(context), input.sourceId, mergeRequestIid, discussion.id, note, null, position);\n };\n\n const updateReviewComment: VersionControl['updateReviewComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const reference = parseDiscussionNoteId(input.commentId);\n const discussion = await context.api.getMergeRequestDiscussion(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n );\n const position = discussionPosition(discussion.notes);\n if (!position) throw new GitLabApiError('GitLab discussion is not anchored to a diff.', 400);\n const root = discussion.notes.find(candidate => candidate.position) ?? discussion.notes[0];\n if (!root) throw new GitLabApiError('GitLab discussion response did not include a note.', 502);\n const note = await context.api.updateMergeRequestDiscussionNote(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n reference.noteId,\n input.body,\n );\n return toReviewComment(\n webBaseUrl(context),\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n note,\n note.id === root.id ? null : packDiscussionNoteId(reference.mergeRequestIid, reference.discussionId, root.id),\n position,\n );\n };\n\n const deleteReviewComment: VersionControl['deleteReviewComment'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const reference = parseDiscussionNoteId(input.commentId);\n await context.api.deleteMergeRequestDiscussionNote(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n reference.noteId,\n );\n };\n\n const resolveReviewThread: NonNullable<VersionControl['resolveReviewThread']> = async input => {\n const context = await deps.contextForConnection(input.connection);\n const reference = parseDiscussionNoteId(input.commentId);\n await context.api.resolveMergeRequestDiscussion(\n input.sourceId,\n reference.mergeRequestIid,\n reference.discussionId,\n input.resolved,\n );\n };\n\n const publishCommitCheck: NonNullable<VersionControl['publishCommitCheck']> = async input => {\n const context = await deps.contextForConnection(input.connection);\n const body = {\n state: GITLAB_COMMIT_STATUS_STATE[input.status],\n name: input.name,\n ...(input.ref !== undefined ? { ref: input.ref } : {}),\n // GitLab commit statuses carry a single short description, so the title is the only text published.\n description: input.title.slice(0, GITLAB_COMMIT_STATUS_DESCRIPTION_LIMIT),\n };\n // GitLab answers 409 when two updates to the same status race; a short bounded retry settles it.\n let status;\n for (let attempt = 1; ; attempt++) {\n try {\n status = await context.api.setCommitStatus(input.sourceId, input.headSha, body);\n break;\n } catch (error) {\n if (attempt >= 3 || (error as { status?: number } | null)?.status !== 409) throw error;\n await new Promise(resolve => setTimeout(resolve, 250 * attempt));\n }\n }\n return { id: String(status.id), name: status.name, headSha: status.sha, status: input.status, url: status.target_url };\n };\n\n const listRequestedReviewers: VersionControl['listRequestedReviewers'] = async input => {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequest = await context.api.getMergeRequest(\n input.sourceId,\n requirePositiveId(input.pullRequestId, 'merge request'),\n );\n return toRequestedReviewers(mergeRequest);\n };\n\n const requestReviewers: VersionControl['requestReviewers'] = async input => {\n rejectTeamReviewers(input.teams);\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const mergeRequest = await context.api.getMergeRequest(input.sourceId, mergeRequestIid);\n const requestedMembers = await resolveMembers(context.api, input.sourceId, input.users ?? []);\n const currentIds = await reviewerIds(context.api, input.sourceId, mergeRequest.reviewers ?? []);\n const reviewerIdsToSet = [...new Set([...currentIds, ...requestedMembers.map(member => member.id)])];\n return toRequestedReviewers(\n await context.api.setMergeRequestReviewers(input.sourceId, mergeRequestIid, reviewerIdsToSet),\n );\n };\n\n const removeRequestedReviewers: VersionControl['removeRequestedReviewers'] = async input => {\n rejectTeamReviewers(input.teams);\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n const mergeRequest = await context.api.getMergeRequest(input.sourceId, mergeRequestIid);\n const removals = new Set((input.users ?? []).map(username => username.toLowerCase()));\n const retained = (mergeRequest.reviewers ?? []).filter(user => !removals.has(user.username.toLowerCase()));\n const retainedIds = await reviewerIds(context.api, input.sourceId, retained);\n return toRequestedReviewers(\n await context.api.setMergeRequestReviewers(input.sourceId, mergeRequestIid, retainedIds),\n );\n };\n\n return {\n initialize: input => {\n storage = input.storage;\n },\n registerInstallation: async ({ orgId, userId, installation }) => {\n const requestedConnection = parseConnection(installation.metadata?.connection);\n if (!requestedConnection) {\n throw new GitLabApiError('GitLab installation metadata must include a valid connection.', 400);\n }\n const context = await deps.contextForConnection(requestedConnection);\n return sourceControlStorage().installations.upsert({\n orgId,\n connectedByUserId: userId,\n externalId: installation.externalId,\n accountName: installation.accountName,\n accountType: installation.accountType,\n providerMetadata: {\n ...installation.metadata,\n connection: context.connection,\n host: normalizeHost(context.host),\n },\n });\n },\n registerRepositories: async ({ orgId, installationId, repositories }) => {\n const normalizedRepositories = repositories.map(repository => ({\n ...repository,\n slug: normalizeSlug(repository.slug),\n }));\n return await Promise.all(\n normalizedRepositories.map(repository =>\n sourceControlStorage().repositories.upsert({\n orgId,\n input: {\n installationId,\n externalId: repository.externalId,\n slug: repository.slug,\n defaultBranch: repository.defaultBranch,\n providerMetadata: repository.metadata,\n },\n }),\n ),\n );\n },\n getRepositoryTarget: async ({ orgId, repositoryId }) => {\n const repository = await sourceControlStorage().repositories.get({ orgId, id: repositoryId });\n if (!repository) throw new Error('Version-control repository not found.');\n const installation = await sourceControlStorage().installations.get({\n orgId,\n id: repository.installationId,\n });\n if (!installation) throw new Error('Version-control installation not found.');\n const connection = parseConnection(installation.providerMetadata.connection);\n if (!connection) throw new GitLabApiError('GitLab installation connection metadata is invalid.', 500);\n const context = await (deps.contextForStoredInstallation ?? deps.contextForConnection)(\n connection,\n typeof installation.providerMetadata.host === 'string' ? installation.providerMetadata.host : undefined,\n );\n // The API uses GitLab's immutable numeric project ID because Platform's\n // proxy normalizes encoded path slashes; retain the slug for browser URLs.\n return { connection: context.connection, sourceId: `${repository.externalId}:${repository.slug}` };\n },\n getRepositoryAccess: async ({ orgId, repositoryId }) => {\n const repository = await sourceControlStorage().repositories.get({ orgId, id: repositoryId });\n if (!repository) throw new Error('Version-control repository not found.');\n const installation = await sourceControlStorage().installations.get({\n orgId,\n id: repository.installationId,\n });\n if (!installation) throw new Error('Version-control installation not found.');\n const connection = parseConnection(installation.providerMetadata.connection);\n if (!connection) throw new GitLabApiError('GitLab installation connection metadata is invalid.', 500);\n const context = await (deps.contextForStoredInstallation ?? deps.contextForConnection)(\n connection,\n typeof installation.providerMetadata.host === 'string' ? installation.providerMetadata.host : undefined,\n );\n if (!context.repositoryAccessToken) {\n throw notSupported('This GitLab connection does not expose credentials for repository cloning.');\n }\n const token = await context.repositoryAccessToken();\n const baseUrl = webBaseUrl(context);\n const slug = normalizeSlug(repository.slug);\n return {\n cloneUrl: `${baseUrl}/${slug}.git`,\n authorization: { scheme: 'bearer', token, username: 'oauth2' },\n };\n },\n listPullRequests,\n getPullRequest,\n createPullRequest,\n updatePullRequest,\n closePullRequest,\n mergePullRequest,\n listComments,\n resolveReviewThread,\n createComment,\n updateComment,\n deleteComment,\n listReviews,\n getReview,\n createReview,\n updateReview,\n submitReview,\n dismissReview,\n deletePendingReview,\n listReviewComments,\n createReviewComment,\n updateReviewComment,\n deleteReviewComment,\n listRequestedReviewers,\n requestReviewers,\n removeRequestedReviewers,\n publishCommitCheck,\n };\n}\n\nconst GITLAB_COMMIT_STATUS_DESCRIPTION_LIMIT = 255;\n\n// GitLab has no neutral state; an informational result is reported as skipped rather than as a pass.\nconst GITLAB_COMMIT_STATUS_STATE: Record<CommitCheckStatus, GitLabCommitStatusState> = {\n 'in-progress': 'running',\n success: 'success',\n failure: 'failed',\n neutral: 'skipped',\n cancelled: 'canceled',\n skipped: 'skipped',\n};\n\nasync function submitReviewAction(\n deps: GitLabVersionControlDependencies,\n input: {\n connection: IntegrationConnection;\n sourceId: string;\n pullRequestId: string;\n event: 'approve' | 'request-changes' | 'comment' | undefined;\n body?: string;\n commitId?: string;\n },\n): Promise<Review> {\n const context = await deps.contextForConnection(input.connection);\n const mergeRequestIid = requirePositiveId(input.pullRequestId, 'merge request');\n if (input.event === 'request-changes') {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab has no first-class request-changes review event.');\n }\n if (input.event === undefined) {\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab has no first-class pending review object.');\n }\n if (input.event === 'approve') {\n const body = input.body?.trim();\n await context.api.approveMergeRequest(input.sourceId, mergeRequestIid, input.commitId);\n const comment = body\n ? toPullRequestComment(\n webBaseUrl(context),\n input.sourceId,\n mergeRequestIid,\n await context.api.createMergeRequestNote(input.sourceId, mergeRequestIid, body),\n )\n : null;\n return {\n id: String(mergeRequestIid) + ':approval',\n url: comment?.url ?? mergeRequestUrl(webBaseUrl(context), input.sourceId, mergeRequestIid),\n author: comment?.author ?? null,\n body: comment?.body ?? null,\n state: 'approved',\n commitId: null,\n submittedAt: comment?.createdAt ?? null,\n };\n }\n const body = input.body?.trim();\n if (!body) throw new GitLabApiError('GitLab comment reviews require a body.', 400);\n const note = await context.api.createMergeRequestNote(input.sourceId, mergeRequestIid, body);\n return toCommentReview(webBaseUrl(context), input.sourceId, mergeRequestIid, note);\n}\n\nfunction toCommentReview(host: string, sourceId: string, mergeRequestIid: number, note: GitLabNote): Review {\n const comment = toPullRequestComment(host, sourceId, mergeRequestIid, note);\n return {\n id: `${mergeRequestIid}:comment:${note.id}`,\n url: comment.url,\n author: comment.author,\n body: comment.body,\n state: 'commented',\n commitId: null,\n submittedAt: note.created_at,\n };\n}\n\nfunction toApprovalReview(host: string, sourceId: string, mergeRequestIid: number, user: GitLabUser): Review {\n return {\n id: `${mergeRequestIid}:approval:${user.id ?? user.username}`,\n url: mergeRequestUrl(host, sourceId, mergeRequestIid),\n author: displayName(user),\n body: null,\n state: 'approved',\n commitId: null,\n submittedAt: null,\n };\n}\n\nfunction toReviewComment(\n host: string,\n sourceId: string,\n mergeRequestIid: number,\n discussionId: string,\n note: GitLabDiscussionNote,\n replyToId: string | null,\n fallbackPosition?: GitLabDiscussionPosition,\n): ReviewComment {\n const position = note.position ?? fallbackPosition;\n if (!position) throw new GitLabApiError('GitLab review comment is missing its diff position.', 502);\n const base = toPullRequestComment(host, sourceId, mergeRequestIid, note);\n const side = position.new_line !== null && position.new_line !== undefined ? 'right' : 'left';\n return {\n ...base,\n id: packDiscussionNoteId(mergeRequestIid, discussionId, note.id),\n path: side === 'right' ? position.new_path : position.old_path,\n line: side === 'right' ? (position.new_line ?? null) : (position.old_line ?? null),\n side,\n commitId: position.head_sha,\n replyToId,\n };\n}\n\nfunction mergeRequestUrl(host: string, sourceId: string, mergeRequestIid: number): string {\n return `${host}/${sourcePath(sourceId)}/-/merge_requests/${mergeRequestIid}`;\n}\n\nfunction webBaseUrl(context: GitLabVersionControlContext): string {\n const host = normalizeHost(context.host);\n if (!context.webBaseUrl) return `https://${host}`;\n let url: URL;\n try {\n url = new URL(context.webBaseUrl);\n } catch {\n throw new GitLabApiError('GitLab web base URL is invalid.', 400);\n }\n const loopback =\n url.hostname === 'localhost' || url.hostname === '[::1]' || /^127(?:\\.\\d{1,3}){3}$/.test(url.hostname);\n if (\n url.host !== host ||\n (url.protocol !== 'https:' && !(url.protocol === 'http:' && loopback)) ||\n url.username ||\n url.password ||\n url.search ||\n url.hash\n ) {\n throw new GitLabApiError('GitLab web base URL is invalid.', 400);\n }\n return `${url.origin}${url.pathname.replace(/\\/+$/, '')}`;\n}\n\nfunction sourcePath(sourceId: string): string {\n const match = /^\\d+:(.+)$/.exec(sourceId);\n return normalizeSlug(match?.[1] ?? sourceId);\n}\n\nfunction packDiscussionId(mergeRequestIid: number, discussionId: string): string {\n if (!discussionId || discussionId.includes(':')) {\n throw new GitLabApiError('GitLab discussion id is invalid.', 400);\n }\n return `${mergeRequestIid}:${discussionId}`;\n}\n\nfunction packDiscussionNoteId(mergeRequestIid: number, discussionId: string, noteId: number): string {\n return `${packDiscussionId(mergeRequestIid, discussionId)}:${noteId}`;\n}\n\nfunction parseReviewReplyId(value: string): { mergeRequestIid: number; discussionId: string } {\n const parts = value.split(':');\n if (parts.length === 2) return parseDiscussionId(value);\n if (parts.length === 3 && parts[1]) {\n requirePositiveId(parts[2]!, 'discussion note');\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n discussionId: parts[1],\n };\n }\n throw new GitLabApiError('GitLab review reply id is invalid.', 400);\n}\n\nfunction parseDiscussionId(value: string): { mergeRequestIid: number; discussionId: string } {\n const parts = value.split(':');\n if (parts.length !== 2 || !parts[1]) throw new GitLabApiError('GitLab discussion id is invalid.', 400);\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n discussionId: parts[1],\n };\n}\n\nfunction parseDiscussionNoteId(value: string): { mergeRequestIid: number; discussionId: string; noteId: number } {\n const parts = value.split(':');\n if (parts.length !== 3 || !parts[1]) throw new GitLabApiError('GitLab discussion note id is invalid.', 400);\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n discussionId: parts[1],\n noteId: requirePositiveId(parts[2]!, 'discussion note'),\n };\n}\n\nfunction discussionPosition(notes: GitLabDiscussionNote[]): GitLabDiscussionPosition | undefined {\n return notes.find(note => note.position)?.position ?? undefined;\n}\n\nasync function discussionLine(path: string, line: number, side: 'left' | 'right') {\n const pathHash = Buffer.from(await globalThis.crypto.subtle.digest('SHA-1', new TextEncoder().encode(path))).toString(\n 'hex',\n );\n const oldLine = side === 'left' ? line : undefined;\n const newLine = side === 'right' ? line : undefined;\n return {\n line_code: `${pathHash}_${oldLine ?? 0}_${newLine ?? 0}`,\n type: side === 'left' ? ('old' as const) : ('new' as const),\n old_line: oldLine,\n new_line: newLine,\n };\n}\n\nfunction toRequestedReviewers(mergeRequest: GitLabMergeRequest): RequestedReviewers {\n return {\n users: (mergeRequest.reviewers ?? []).map(user => user.username),\n teams: [],\n };\n}\n\nasync function resolveMembers(api: GitLabApiClient, sourceId: string, usernames: string[]): Promise<GitLabMember[]> {\n return Promise.all(usernames.map(username => resolveMember(api, sourceId, username)));\n}\n\nasync function resolveMember(api: GitLabApiClient, sourceId: string, username: string): Promise<GitLabMember> {\n const normalized = username.trim();\n if (!normalized) throw new GitLabApiError('GitLab reviewer username is invalid.', 400);\n const members = await api.listProjectMembers(sourceId, { query: normalized });\n const member = members.find(candidate => candidate.username.toLowerCase() === normalized.toLowerCase());\n if (!member) throw new GitLabApiError(`GitLab reviewer ${normalized} is not a project member.`, 404);\n return member;\n}\n\nasync function reviewerIds(api: GitLabApiClient, sourceId: string, users: GitLabUser[]): Promise<number[]> {\n return Promise.all(users.map(async user => user.id ?? (await resolveMember(api, sourceId, user.username)).id));\n}\n\nfunction rejectTeamReviewers(teams: string[] | undefined): void {\n if (!teams?.length) return;\n // FLAGGED FOR MANUAL REVIEW (spec §6.2)\n throw notSupported('GitLab merge requests do not have a direct team-reviewer equivalent.');\n}\n\nfunction notSupported(message: string): GitLabApiError {\n return new GitLabApiError(message, 501);\n}\n\nfunction toPullRequest(mergeRequest: GitLabMergeRequest): PullRequest {\n return {\n id: String(mergeRequest.iid),\n title: mergeRequest.title,\n url: mergeRequest.web_url,\n author: displayName(mergeRequest.author),\n assignees: mergeRequest.assignees?.map(user => user.username),\n requestedReviewers: mergeRequest.reviewers?.map(user => user.username),\n labels: mergeRequest.labels,\n body: mergeRequest.description?.trim() ? mergeRequest.description : null,\n state: mergeRequest.state === 'closed' || mergeRequest.state === 'merged' ? 'closed' : 'open',\n draft: isDraft(mergeRequest),\n merged: mergeRequest.state === 'merged' || Boolean(mergeRequest.merged_at),\n mergeable: mergeableState(mergeRequest.merge_status),\n baseBranch: mergeRequest.target_branch,\n headBranch: mergeRequest.source_branch,\n headSha: mergeRequest.sha,\n createdAt: mergeRequest.created_at,\n updatedAt: mergeRequest.updated_at,\n };\n}\n\nfunction toPullRequestComment(\n host: string,\n sourceId: string,\n mergeRequestIid: number,\n note: GitLabNote,\n): PullRequestComment {\n return {\n id: `${mergeRequestIid}:${note.id}`,\n url: `${mergeRequestUrl(host, sourceId, mergeRequestIid)}#note_${note.id}`,\n author: displayName(note.author),\n body: note.body,\n createdAt: note.created_at,\n updatedAt: note.updated_at ?? note.created_at,\n };\n}\n\nfunction displayName(user: { name?: string | null; username: string } | null | undefined): string | null {\n return user?.username || user?.name?.trim() || null;\n}\n\nfunction isDraft(mergeRequest: GitLabMergeRequest): boolean {\n return mergeRequest.draft ?? mergeRequest.work_in_progress ?? /^(?:draft:|\\[draft\\])/i.test(mergeRequest.title);\n}\n\nfunction mergeableState(status: string | undefined): boolean | null {\n if (status === 'can_be_merged') return true;\n if (status === 'cannot_be_merged') return false;\n return null;\n}\n\nfunction combinedCommitMessage(title: string | undefined, body: string | undefined): string | undefined {\n const parts = [title?.trim(), body?.trim()].filter((part): part is string => Boolean(part));\n return parts.length > 0 ? parts.join('\\n\\n') : undefined;\n}\n\nfunction parseNoteId(value: string): { mergeRequestIid: number; noteId: number } {\n const parts = value.split(':');\n if (parts.length !== 2) throw new GitLabApiError('GitLab merge request note id is invalid.', 400);\n return {\n mergeRequestIid: requirePositiveId(parts[0]!, 'merge request'),\n noteId: requirePositiveId(parts[1]!, 'note'),\n };\n}\n\nfunction parsePositiveCursor(cursor: string | undefined): number {\n if (cursor === undefined) return 1;\n const page = parsePositiveInteger(cursor);\n if (page === null) throw new GitLabApiError('GitLab cursor must be a positive page number.', 400);\n return page;\n}\n\nfunction requirePositiveId(value: string, resource: string): number {\n const parsed = parsePositiveInteger(value);\n if (parsed === null) throw new GitLabApiError(`GitLab ${resource} id must be a positive integer.`, 400);\n return parsed;\n}\n\nfunction parsePositiveInteger(value: string): number | null {\n if (!/^\\d+$/.test(value)) return null;\n const parsed = Number(value);\n return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;\n}\n\nfunction parseConnection(value: unknown): IntegrationConnection | null {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return null;\n const connection = value as Record<string, unknown>;\n if (connection.type === 'oauth' && typeof connection.accessToken === 'string' && connection.accessToken.length > 0) {\n return { type: 'oauth', accessToken: connection.accessToken };\n }\n if (\n connection.type === 'app-installation' &&\n Number.isSafeInteger(connection.installationId) &&\n Number(connection.installationId) > 0\n ) {\n return { type: 'app-installation', installationId: Number(connection.installationId) };\n }\n return null;\n}\n\nfunction normalizeHost(value: string): string {\n const host = value.trim();\n if (!host || host.includes('/') || host.includes('@')) throw new GitLabApiError('GitLab host is invalid.', 400);\n let parsed: URL;\n try {\n parsed = new URL(`https://${host}`);\n } catch {\n throw new GitLabApiError('GitLab host is invalid.', 400);\n }\n if (parsed.host !== host) throw new GitLabApiError('GitLab host is invalid.', 400);\n return host;\n}\n\nfunction normalizeSlug(value: string): string {\n const slug = value.replace(/^\\/+|\\/+$/g, '');\n const segments = slug.split('/');\n if (!slug || segments.some(segment => !/^[A-Za-z0-9_.-]+$/.test(segment) || segment === '.' || segment === '..')) {\n throw new GitLabApiError('GitLab repository slug is invalid.', 400);\n }\n return slug;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { IntegrationConnection } from '../../capabilities/connection.js';
|
|
2
2
|
import type { Intake } from '../../capabilities/intake.js';
|
|
3
|
-
import { IncidentioApiClient } from './api.js';
|
|
3
|
+
import type { IncidentioApiClient } from './api.js';
|
|
4
4
|
export declare const INCIDENTIO_INCIDENTS_SOURCE_ID = "incidentio:incidents";
|
|
5
5
|
export declare const INCIDENTIO_FOLLOW_UPS_SOURCE_ID = "incidentio:follow-ups";
|
|
6
6
|
export declare function createIncidentioIntake(config: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../../src/integrations/incidentio/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAEV,MAAM,EASP,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../../src/integrations/incidentio/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,KAAK,EAEV,MAAM,EASP,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,mBAAmB,EAA2D,MAAM,UAAU,CAAC;AAE7G,eAAO,MAAM,8BAA8B,yBAAyB,CAAC;AACrE,eAAO,MAAM,+BAA+B,0BAA0B,CAAC;AAavE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,GAAG,EAAE,mBAAmB,CAAC;IACzB,UAAU,EAAE,qBAAqB,CAAC;CACnC,GAAG,MAAM,CAoIT"}
|
|
@@ -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":"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"}
|
|
@@ -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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"}
|
|
@@ -15,6 +15,8 @@ import { Octokit } from "@octokit/rest";
|
|
|
15
15
|
const PAGE_SIZE = 30;
|
|
16
16
|
const OPEN_ISSUE_DISCOVERY_MAX_PAGES = 5;
|
|
17
17
|
const API_PREFIX = "/v1/server";
|
|
18
|
+
const CHECK_TITLE_LIMIT = 255;
|
|
19
|
+
const CHECK_TEXT_LIMIT = 65535;
|
|
18
20
|
/**
|
|
19
21
|
* Slug of the GitHub App this integration posts as. Platform credentials do not
|
|
20
22
|
* carry their own identity, so Factory cannot otherwise recognise its own
|
|
@@ -393,7 +395,8 @@ var PlatformGithubIntegration = class {
|
|
|
393
395
|
deleteReviewComment: (input) => this.#deleteReviewComment(input),
|
|
394
396
|
listRequestedReviewers: (input) => this.#requestedReviewers("GET", input),
|
|
395
397
|
requestReviewers: (input) => this.#requestedReviewers("POST", input),
|
|
396
|
-
removeRequestedReviewers: (input) => this.#requestedReviewers("DELETE", input)
|
|
398
|
+
removeRequestedReviewers: (input) => this.#requestedReviewers("DELETE", input),
|
|
399
|
+
publishCommitCheck: (input) => this.#publishCommitCheck(input)
|
|
397
400
|
};
|
|
398
401
|
constructor(options = {}) {
|
|
399
402
|
this.#rules = resolveGithubRules(options.rules);
|
|
@@ -1041,6 +1044,29 @@ var PlatformGithubIntegration = class {
|
|
|
1041
1044
|
event: input.event ? reviewEvent(input.event) : void 0
|
|
1042
1045
|
}, { actingUserId: input.actingUserId }));
|
|
1043
1046
|
}
|
|
1047
|
+
async #publishCommitCheck(input) {
|
|
1048
|
+
requireGithubConnection(input.connection);
|
|
1049
|
+
const run = await this.#client.request("POST", repositoryPath(input.sourceId, "check-runs"), {
|
|
1050
|
+
name: input.name,
|
|
1051
|
+
headSha: input.headSha,
|
|
1052
|
+
...input.status === "in-progress" ? { status: "in_progress" } : {
|
|
1053
|
+
status: "completed",
|
|
1054
|
+
conclusion: input.status
|
|
1055
|
+
},
|
|
1056
|
+
output: {
|
|
1057
|
+
title: input.title.slice(0, CHECK_TITLE_LIMIT),
|
|
1058
|
+
summary: input.summary.slice(0, CHECK_TEXT_LIMIT),
|
|
1059
|
+
...input.details ? { text: input.details.slice(0, CHECK_TEXT_LIMIT) } : {}
|
|
1060
|
+
}
|
|
1061
|
+
}, { actingUserId: input.actingUserId });
|
|
1062
|
+
return {
|
|
1063
|
+
id: String(run.id),
|
|
1064
|
+
name: input.name,
|
|
1065
|
+
headSha: input.headSha,
|
|
1066
|
+
status: input.status,
|
|
1067
|
+
url: run.htmlUrl
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1044
1070
|
async #updateReview(input) {
|
|
1045
1071
|
return parseReview(await this.#client.request("PUT", `${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, "review")}`, { body: input.body }, { actingUserId: input.actingUserId }));
|
|
1046
1072
|
}
|