@mastra/factory 0.18.0 → 0.19.0-alpha.1
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 +13 -1
- package/dist/boards/registry.d.ts.map +1 -1
- package/dist/boards/registry.js +0 -2
- package/dist/boards/registry.js.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +12 -2
- package/dist/factory.js.map +1 -1
- package/dist/integrations/github/default-rules.d.ts +10 -2
- package/dist/integrations/github/default-rules.d.ts.map +1 -1
- package/dist/integrations/github/default-rules.js +28 -4
- package/dist/integrations/github/default-rules.js.map +1 -1
- package/dist/integrations/github/integration.js +1 -1
- package/dist/integrations/github/reconcile-worker.d.ts.map +1 -1
- package/dist/integrations/github/reconcile-worker.js +1 -2
- package/dist/integrations/github/reconcile-worker.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +2 -15
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +5 -0
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +17 -8
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/stale-reviews.d.ts +22 -0
- package/dist/integrations/github/stale-reviews.d.ts.map +1 -0
- package/dist/integrations/github/stale-reviews.js +70 -0
- package/dist/integrations/github/stale-reviews.js.map +1 -0
- package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
- package/dist/integrations/gitlab/version-control.js +4 -5
- package/dist/integrations/gitlab/version-control.js.map +1 -1
- package/dist/integrations/gitlab/webhook.d.ts.map +1 -1
- package/dist/integrations/gitlab/webhook.js +2 -2
- package/dist/integrations/gitlab/webhook.js.map +1 -1
- package/dist/integrations/issue-reconcile-worker.d.ts.map +1 -1
- package/dist/integrations/issue-reconcile-worker.js +1 -2
- package/dist/integrations/issue-reconcile-worker.js.map +1 -1
- package/dist/integrations/jira/integration.d.ts.map +1 -1
- package/dist/integrations/jira/integration.js +7 -1
- package/dist/integrations/jira/integration.js.map +1 -1
- package/dist/integrations/linear/integration.d.ts.map +1 -1
- package/dist/integrations/linear/integration.js +10 -30
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/github/event-worker.js +1 -2
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.js +1 -1
- package/dist/integrations/platform/gitlab/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/gitlab/event-worker.js +1 -2
- package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
- package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
- package/dist/integrations/platform/jira/integration.js +7 -1
- package/dist/integrations/platform/jira/integration.js.map +1 -1
- package/dist/integrations/platform/linear/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/linear/event-worker.js +1 -2
- package/dist/integrations/platform/linear/event-worker.js.map +1 -1
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +9 -9
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +1 -2
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/review-verdict.d.ts +6 -0
- package/dist/review-verdict.d.ts.map +1 -0
- package/dist/review-verdict.js +14 -0
- package/dist/review-verdict.js.map +1 -0
- package/dist/routes/attention-providers.d.ts +6 -7
- package/dist/routes/attention-providers.d.ts.map +1 -1
- package/dist/routes/attention-providers.js +12 -5
- package/dist/routes/attention-providers.js.map +1 -1
- package/dist/routes/attention.d.ts +2 -0
- package/dist/routes/attention.d.ts.map +1 -1
- package/dist/routes/attention.js +9 -3
- package/dist/routes/attention.js.map +1 -1
- package/dist/routes/oauth.d.ts.map +1 -1
- package/dist/routes/oauth.js +3 -3
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +1 -2
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +10 -5
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/decision-applicability.d.ts +26 -0
- package/dist/rules/decision-applicability.d.ts.map +1 -0
- package/dist/rules/decision-applicability.js +47 -0
- package/dist/rules/decision-applicability.js.map +1 -0
- package/dist/rules/dispatcher.d.ts +4 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +33 -24
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +5 -5
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +1 -2
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +18 -1
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +20 -0
- package/dist/rules/validation.js.map +1 -1
- package/dist/secret-encryption.js +2 -2
- package/dist/secret-encryption.js.map +1 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +1 -2
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/source-control-tools.d.ts +9 -0
- package/dist/session/source-control-tools.d.ts.map +1 -1
- package/dist/session/source-control-tools.js +51 -2
- package/dist/session/source-control-tools.js.map +1 -1
- package/dist/state-signing.js +3 -3
- package/dist/state-signing.js.map +1 -1
- package/dist/storage/domains/audit/base.d.ts.map +1 -1
- package/dist/storage/domains/audit/base.js +2 -3
- package/dist/storage/domains/audit/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +5 -6
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +3 -0
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +15 -2
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/write-tools.d.ts +2 -0
- package/dist/supervisor/write-tools.d.ts.map +1 -1
- package/dist/supervisor/write-tools.js +3 -0
- package/dist/supervisor/write-tools.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +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-rereview/SKILL.md +12 -3
- package/factory-skills/factory-review/SKILL.md +12 -3
- package/package.json +9 -9
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { PlatformApiError } from "../api-client.js";
|
|
2
2
|
import { parseGitLabWebhookBody, processGitLabWebhook } from "../../gitlab/webhook.js";
|
|
3
3
|
import { NoopLeaseProvider, isLeaseProvider } from "@mastra/core/events";
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
5
4
|
import { MastraWorker } from "@mastra/core/worker";
|
|
6
5
|
//#region src/integrations/platform/gitlab/event-worker.ts
|
|
7
6
|
const DEFAULT_POLL_INTERVAL_MS = 2e4;
|
|
@@ -39,7 +38,7 @@ var PlatformGitLabEventWorker = class extends MastraWorker {
|
|
|
39
38
|
#intervalMs;
|
|
40
39
|
#now;
|
|
41
40
|
#process;
|
|
42
|
-
#leaseOwner = randomUUID();
|
|
41
|
+
#leaseOwner = globalThis.crypto.randomUUID();
|
|
43
42
|
#running = false;
|
|
44
43
|
#timer;
|
|
45
44
|
#leaseRenewalTimer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-worker.js","names":["#client","#controller","#gitlab","#storage","#listConnections","#ingestFactoryEvent","#intervalMs","#now","#process","#leaseOwner","#leaseTtlMs","#leaseProvider","#running","#startedAt","#settings","#schedule","#timer","#stopLeaseRenewal","#inFlight","#hasLease","#leaseKey","#tick","#ensureLease","#poll","#startLeaseRenewal","#leaseRenewalTimer","#pollConnection","#saveSettings","#processEvent"],"sources":["../../../../src/integrations/platform/gitlab/event-worker.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport type { MountedMastraCode } from '@mastra/code-sdk';\nimport { isLeaseProvider, NoopLeaseProvider } from '@mastra/core/events';\nimport type { LeaseProvider, PubSub } from '@mastra/core/events';\nimport { MastraWorker } from '@mastra/core/worker';\nimport type { WorkerDeps } from '@mastra/core/worker';\n\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type { GitLabWebhookDispatchIntegration } from '../../gitlab/webhook-dispatch.js';\nimport { parseGitLabWebhookBody, processGitLabWebhook } from '../../gitlab/webhook.js';\nimport type { ParsedGitLabWebhook } from '../../gitlab/webhook.js';\nimport type { PlatformApiClient } from '../api-client.js';\nimport { PlatformApiError } from '../api-client.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 20_000;\nconst EVENT_PAGE_SIZE = 500;\n/**\n * Pages drained per connection per cycle. A connection that never runs dry\n * (a busy group webhook, or a backlog after downtime) hands over to the next\n * connection after this many pages and resumes from its saved cursor next tick.\n */\nconst MAX_PAGES_PER_CONNECTION_PER_CYCLE = 10;\nconst MIN_LEASE_TTL_MS = 30_000;\nconst CURSOR_ORG_ID = '__platform_gitlab_event_worker__';\nconst CURSOR_USER_ID = 'worker';\nconst DELIVERY_ID_PREFIX = 'platform';\n\ntype PlatformGitLabEventWorkerSettings = {\n version: 1;\n connections: Record<string, { afterEventId: string }>;\n};\n\nexport type PlatformGitLabEventStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n PlatformGitLabEventWorkerSettings,\n Record<string, unknown>\n>;\n\n/** One row of the Platform integration event log. */\nexport interface IntegrationEventEntry {\n id: string;\n timestamp: number;\n integrationId: string;\n eventType: string;\n sourceEventId: string | null;\n payload: unknown;\n}\n\nexport interface PlatformGitLabEventConnection {\n id: string;\n status: 'active' | 'needs_reauth';\n}\n\nexport type PlatformGitLabEventDispatchIntegration = GitLabWebhookDispatchIntegration;\n\nexport interface PlatformGitLabEventWorkerConfig {\n client: PlatformApiClient;\n controller: MountedMastraCode['controller'];\n gitlab: PlatformGitLabEventDispatchIntegration;\n storage: PlatformGitLabEventStorage;\n /** Platform connections to tail; only `active` ones are polled. */\n listConnections: () => Promise<PlatformGitLabEventConnection[]>;\n ingestFactoryEvent?: (event: ParsedGitLabWebhook) => Promise<unknown>;\n intervalMs?: number;\n now?: () => number;\n process?: typeof processGitLabWebhook;\n}\n\n/**\n * Tails the Platform event log for every active GitLab connection and feeds\n * each stored webhook body through the same ingress the direct\n * `/web/gitlab/webhook` route uses. A Platform-managed connection has no\n * project webhook pointing at Factory, so this is how issue, note, merge\n * request and push events reach the rules engine and subscribed sessions.\n *\n * One replica polls at a time (lease). Each connection keeps its own cursor,\n * persisted only after a whole page has been processed, so a crash mid-page\n * replays the page rather than losing it; replay handling in the rules ingress\n * and the delivery-id dedupe keys make that safe.\n */\nexport class PlatformGitLabEventWorker extends MastraWorker {\n readonly name = 'platform-gitlab-events';\n\n readonly #client: PlatformApiClient;\n readonly #controller: MountedMastraCode['controller'];\n readonly #gitlab: PlatformGitLabEventDispatchIntegration;\n readonly #storage: PlatformGitLabEventStorage;\n readonly #listConnections: () => Promise<PlatformGitLabEventConnection[]>;\n readonly #ingestFactoryEvent: ((event: ParsedGitLabWebhook) => Promise<unknown>) | undefined;\n readonly #intervalMs: number;\n readonly #now: () => number;\n readonly #process: typeof processGitLabWebhook;\n readonly #leaseOwner = randomUUID();\n\n #running = false;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #leaseRenewalTimer: ReturnType<typeof setInterval> | undefined;\n #inFlight: Promise<void> | undefined;\n #leaseProvider: LeaseProvider = NoopLeaseProvider;\n #leaseTtlMs: number;\n #hasLease = false;\n #startedAt = 0;\n #settings: PlatformGitLabEventWorkerSettings = { version: 1, connections: {} };\n\n constructor(config: PlatformGitLabEventWorkerConfig) {\n super();\n this.#client = config.client;\n this.#controller = config.controller;\n this.#gitlab = config.gitlab;\n this.#storage = config.storage;\n this.#listConnections = config.listConnections;\n this.#ingestFactoryEvent = config.ingestFactoryEvent;\n this.#intervalMs = config.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n throw new Error('Platform GitLab event polling interval must be a positive number.');\n }\n this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);\n this.#now = config.now ?? Date.now;\n this.#process = config.process ?? processGitLabWebhook;\n }\n\n async init(deps: WorkerDeps): Promise<void> {\n await super.init(deps);\n this.#leaseProvider = getLeaseProvider(deps.pubsub);\n }\n\n async start(): Promise<void> {\n if (this.#running) return;\n if (!this.deps) throw new Error('PlatformGitLabEventWorker: call init() before start()');\n\n this.#startedAt = this.#now() - 1;\n this.#settings = normalizeSettings(await this.#storage.settings.get(CURSOR_ORG_ID, CURSOR_USER_ID));\n this.#running = true;\n this.deps.logger.info('Platform GitLab event polling started', {\n intervalMs: this.#intervalMs,\n leaseTtlMs: this.#leaseTtlMs,\n });\n this.#schedule(0);\n }\n\n async stop(): Promise<void> {\n if (!this.#running) return;\n this.#running = false;\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n this.#stopLeaseRenewal();\n await this.#inFlight;\n if (this.#hasLease) {\n await this.#leaseProvider.releaseLease(this.#leaseKey(), this.#leaseOwner).catch(() => undefined);\n this.#hasLease = false;\n }\n }\n\n get isRunning(): boolean {\n return this.#running;\n }\n\n #schedule(delayMs: number): void {\n if (!this.#running) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const run = this.#tick();\n this.#inFlight = run;\n void run.finally(() => {\n if (this.#inFlight === run) this.#inFlight = undefined;\n });\n }, delayMs);\n this.#timer.unref?.();\n }\n\n async #tick(): Promise<void> {\n let nextDelay = this.#intervalMs;\n try {\n if (!(await this.#ensureLease())) return;\n nextDelay = await this.#poll();\n } catch (error) {\n nextDelay = retryDelay(error, this.#intervalMs);\n this.deps?.logger.error('Platform GitLab event polling cycle failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: nextDelay,\n });\n } finally {\n this.#schedule(nextDelay);\n }\n }\n\n async #ensureLease(): Promise<boolean> {\n if (this.#hasLease) return true;\n const result = await this.#leaseProvider.acquireLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs);\n this.#hasLease = result.acquired;\n if (this.#hasLease) this.#startLeaseRenewal();\n return this.#hasLease;\n }\n\n #startLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) return;\n this.#leaseRenewalTimer = setInterval(\n () => {\n void this.#leaseProvider\n .renewLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs)\n .then(renewed => {\n if (!renewed) {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n }\n })\n .catch(error => {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n this.deps?.logger.warn('Platform GitLab event polling lease renewal failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n Math.floor(this.#leaseTtlMs / 3),\n );\n this.#leaseRenewalTimer.unref?.();\n }\n\n #stopLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) clearInterval(this.#leaseRenewalTimer);\n this.#leaseRenewalTimer = undefined;\n }\n\n async #poll(): Promise<number> {\n const connections = (await this.#listConnections()).filter(connection => connection.status === 'active');\n let retryInMs = this.#intervalMs;\n\n for (const connection of connections) {\n if (!this.#running || !this.#hasLease) break;\n try {\n await this.#pollConnection(connection.id);\n } catch (error) {\n if (error instanceof PlatformApiError && (error.status === 401 || error.status === 404)) {\n // Platform no longer serves this connection (revoked, expired or\n // deleted). Its status will read needs_reauth on the next listing;\n // nothing to retry here, so the other connections keep flowing.\n this.deps?.logger.warn('Platform GitLab connection needs reauthorization; skipped this cycle', {\n connectionId: connection.id,\n status: error.status,\n });\n continue;\n }\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform GitLab connection event polling failed', {\n connectionId: connection.id,\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n if (error instanceof PlatformApiError && error.status === 429) break;\n }\n }\n return retryInMs;\n }\n\n async #pollConnection(connectionId: string): Promise<void> {\n if (!this.#settings.connections[connectionId]) {\n // Event ids are Redis stream ids (`<unix ms>-<sequence>`), so a\n // synthesized id at start time skips history the same way the GitHub\n // worker's start timestamp does. Events that arrived before Factory was\n // deployed are not work Factory was asked to do.\n this.#settings.connections[connectionId] = { afterEventId: `${this.#startedAt}-0` };\n await this.#saveSettings();\n }\n\n for (let pageIndex = 0; this.#running && this.#hasLease; pageIndex += 1) {\n if (pageIndex >= MAX_PAGES_PER_CONNECTION_PER_CYCLE) {\n this.deps?.logger.debug('Platform GitLab connection page budget reached; resuming next cycle', {\n connectionId,\n pages: pageIndex,\n });\n return;\n }\n const cursor: { afterEventId: string } = this.#settings.connections[connectionId]!;\n const query = new URLSearchParams({ afterEventId: cursor.afterEventId, limit: String(EVENT_PAGE_SIZE) });\n const pollStartedAt = performance.now();\n const page = await this.#client.request<{ events: IntegrationEventEntry[]; nextCursor: string | null }>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/events?${query}`,\n );\n this.deps?.logger.debug('Platform GitLab connection event poll completed', {\n connectionId,\n eventCount: page.events.length,\n latencyMs: Math.round(performance.now() - pollStartedAt),\n });\n if (page.events.length === 0 || !page.nextCursor) return;\n\n for (const event of page.events) {\n if (!this.#running || !this.#hasLease) return;\n await this.#processEvent(connectionId, event);\n }\n\n if (page.nextCursor === cursor.afterEventId) return;\n // A replica that lost its lease while processing this page must not\n // write a cursor over one the new holder has already advanced. The\n // page is replayed there; delivery is deduplicated by delivery id.\n if (!this.#hasLease) return;\n this.#settings.connections[connectionId] = { afterEventId: page.nextCursor };\n await this.#saveSettings();\n }\n }\n\n async #processEvent(connectionId: string, event: IntegrationEventEntry): Promise<void> {\n if (!event.id) {\n this.deps?.logger.warn('Platform GitLab event log returned an event without an id', { connectionId });\n return;\n }\n const deliveryId = `${DELIVERY_ID_PREFIX}:${connectionId}:${event.id}`;\n const parsed = parseGitLabWebhookBody(event.payload, deliveryId);\n if (!parsed) {\n // Pipelines, deployments, wiki edits and the like: acknowledged and\n // dropped, exactly as the direct route treats an unsupported event header.\n this.deps?.logger.debug('Platform GitLab event ignored: unsupported or malformed body', {\n connectionId,\n eventId: event.id,\n eventType: event.eventType,\n });\n return;\n }\n\n const result = await this.#process(parsed, {\n ingestFactoryEvent: this.#ingestFactoryEvent,\n controller: this.#controller,\n // The integration supplies subscription storage and the project-member\n // trust check, so the dispatcher's default author gate applies unchanged.\n gitlab: this.#gitlab,\n sourceConnectionId: connectionId,\n onConnectionMismatch: subscription => {\n // Expected whenever two connections reach the same project: the\n // event is delivered by the connection that created the subscription.\n this.deps?.logger.debug('Platform GitLab event skipped: subscription belongs to another connection', {\n deliveryId,\n subscriptionId: subscription.id,\n subscriptionConnectionId: subscription.data.installationExternalId,\n });\n },\n onTargetSkipped: subscription => {\n // Routine when a subscription's thread belongs to another deployment,\n // so this stays at debug rather than warning on a loop.\n this.deps?.logger.debug('Platform GitLab event skipped: thread is not held here', {\n deliveryId,\n subscriptionId: subscription.id,\n threadId: subscription.threadId,\n });\n },\n onSenderRejected: notification => {\n this.deps?.logger.debug('Platform GitLab event dropped: sender not authorized', {\n deliveryId,\n repository: notification.metadata.projectPath,\n sender: notification.metadata.sender,\n kind: notification.kind,\n });\n },\n onTargetError: (subscription, error) => {\n this.deps?.logger.error('Platform GitLab event delivery failed for a subscription', {\n deliveryId,\n subscriptionId: subscription.id,\n resourceId: subscription.resourceId,\n threadId: subscription.threadId,\n error: error instanceof Error ? error.message : String(error),\n });\n },\n });\n if (result.status !== 202) {\n this.deps?.logger.warn('Platform GitLab event was not accepted by the webhook processor', {\n connectionId,\n deliveryId,\n status: result.status,\n });\n }\n }\n\n async #saveSettings(): Promise<void> {\n await this.#storage.settings.save(CURSOR_ORG_ID, CURSOR_USER_ID, this.#settings);\n }\n\n #leaseKey(): string {\n return `${this.name}:${this.#storage.integrationId}`;\n }\n}\n\nfunction getLeaseProvider(pubsub: PubSub): LeaseProvider {\n const getProvider = (pubsub as PubSub & { getLeaseProvider?: () => LeaseProvider | undefined }).getLeaseProvider;\n if (typeof getProvider === 'function') return getProvider.call(pubsub) ?? NoopLeaseProvider;\n return isLeaseProvider(pubsub) ? pubsub : NoopLeaseProvider;\n}\n\nfunction normalizeSettings(value: PlatformGitLabEventWorkerSettings | null): PlatformGitLabEventWorkerSettings {\n if (!value || value.version !== 1 || !value.connections || typeof value.connections !== 'object') {\n return { version: 1, connections: {} };\n }\n const connections: PlatformGitLabEventWorkerSettings['connections'] = {};\n for (const [connectionId, cursor] of Object.entries(value.connections)) {\n if (typeof cursor?.afterEventId === 'string' && cursor.afterEventId.length > 0) {\n connections[connectionId] = { afterEventId: cursor.afterEventId };\n }\n }\n return { version: 1, connections };\n}\n\nfunction retryDelay(error: unknown, fallbackMs: number): number {\n if (error instanceof PlatformApiError && error.status === 429 && error.retryAfterSeconds !== null) {\n return Math.max(fallbackMs, error.retryAfterSeconds * 1_000);\n }\n return fallbackMs;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"event-worker.js","names":["#client","#controller","#gitlab","#storage","#listConnections","#ingestFactoryEvent","#intervalMs","#now","#process","#leaseOwner","#leaseTtlMs","#leaseProvider","#running","#startedAt","#settings","#schedule","#timer","#stopLeaseRenewal","#inFlight","#hasLease","#leaseKey","#tick","#ensureLease","#poll","#startLeaseRenewal","#leaseRenewalTimer","#pollConnection","#saveSettings","#processEvent"],"sources":["../../../../src/integrations/platform/gitlab/event-worker.ts"],"sourcesContent":["import type { MountedMastraCode } from '@mastra/code-sdk';\nimport { isLeaseProvider, NoopLeaseProvider } from '@mastra/core/events';\nimport type { LeaseProvider, PubSub } from '@mastra/core/events';\nimport { MastraWorker } from '@mastra/core/worker';\nimport type { WorkerDeps } from '@mastra/core/worker';\n\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type { GitLabWebhookDispatchIntegration } from '../../gitlab/webhook-dispatch.js';\nimport { parseGitLabWebhookBody, processGitLabWebhook } from '../../gitlab/webhook.js';\nimport type { ParsedGitLabWebhook } from '../../gitlab/webhook.js';\nimport type { PlatformApiClient } from '../api-client.js';\nimport { PlatformApiError } from '../api-client.js';\n\nconst DEFAULT_POLL_INTERVAL_MS = 20_000;\nconst EVENT_PAGE_SIZE = 500;\n/**\n * Pages drained per connection per cycle. A connection that never runs dry\n * (a busy group webhook, or a backlog after downtime) hands over to the next\n * connection after this many pages and resumes from its saved cursor next tick.\n */\nconst MAX_PAGES_PER_CONNECTION_PER_CYCLE = 10;\nconst MIN_LEASE_TTL_MS = 30_000;\nconst CURSOR_ORG_ID = '__platform_gitlab_event_worker__';\nconst CURSOR_USER_ID = 'worker';\nconst DELIVERY_ID_PREFIX = 'platform';\n\ntype PlatformGitLabEventWorkerSettings = {\n version: 1;\n connections: Record<string, { afterEventId: string }>;\n};\n\nexport type PlatformGitLabEventStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n PlatformGitLabEventWorkerSettings,\n Record<string, unknown>\n>;\n\n/** One row of the Platform integration event log. */\nexport interface IntegrationEventEntry {\n id: string;\n timestamp: number;\n integrationId: string;\n eventType: string;\n sourceEventId: string | null;\n payload: unknown;\n}\n\nexport interface PlatformGitLabEventConnection {\n id: string;\n status: 'active' | 'needs_reauth';\n}\n\nexport type PlatformGitLabEventDispatchIntegration = GitLabWebhookDispatchIntegration;\n\nexport interface PlatformGitLabEventWorkerConfig {\n client: PlatformApiClient;\n controller: MountedMastraCode['controller'];\n gitlab: PlatformGitLabEventDispatchIntegration;\n storage: PlatformGitLabEventStorage;\n /** Platform connections to tail; only `active` ones are polled. */\n listConnections: () => Promise<PlatformGitLabEventConnection[]>;\n ingestFactoryEvent?: (event: ParsedGitLabWebhook) => Promise<unknown>;\n intervalMs?: number;\n now?: () => number;\n process?: typeof processGitLabWebhook;\n}\n\n/**\n * Tails the Platform event log for every active GitLab connection and feeds\n * each stored webhook body through the same ingress the direct\n * `/web/gitlab/webhook` route uses. A Platform-managed connection has no\n * project webhook pointing at Factory, so this is how issue, note, merge\n * request and push events reach the rules engine and subscribed sessions.\n *\n * One replica polls at a time (lease). Each connection keeps its own cursor,\n * persisted only after a whole page has been processed, so a crash mid-page\n * replays the page rather than losing it; replay handling in the rules ingress\n * and the delivery-id dedupe keys make that safe.\n */\nexport class PlatformGitLabEventWorker extends MastraWorker {\n readonly name = 'platform-gitlab-events';\n\n readonly #client: PlatformApiClient;\n readonly #controller: MountedMastraCode['controller'];\n readonly #gitlab: PlatformGitLabEventDispatchIntegration;\n readonly #storage: PlatformGitLabEventStorage;\n readonly #listConnections: () => Promise<PlatformGitLabEventConnection[]>;\n readonly #ingestFactoryEvent: ((event: ParsedGitLabWebhook) => Promise<unknown>) | undefined;\n readonly #intervalMs: number;\n readonly #now: () => number;\n readonly #process: typeof processGitLabWebhook;\n readonly #leaseOwner = globalThis.crypto.randomUUID();\n\n #running = false;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #leaseRenewalTimer: ReturnType<typeof setInterval> | undefined;\n #inFlight: Promise<void> | undefined;\n #leaseProvider: LeaseProvider = NoopLeaseProvider;\n #leaseTtlMs: number;\n #hasLease = false;\n #startedAt = 0;\n #settings: PlatformGitLabEventWorkerSettings = { version: 1, connections: {} };\n\n constructor(config: PlatformGitLabEventWorkerConfig) {\n super();\n this.#client = config.client;\n this.#controller = config.controller;\n this.#gitlab = config.gitlab;\n this.#storage = config.storage;\n this.#listConnections = config.listConnections;\n this.#ingestFactoryEvent = config.ingestFactoryEvent;\n this.#intervalMs = config.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n throw new Error('Platform GitLab event polling interval must be a positive number.');\n }\n this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);\n this.#now = config.now ?? Date.now;\n this.#process = config.process ?? processGitLabWebhook;\n }\n\n async init(deps: WorkerDeps): Promise<void> {\n await super.init(deps);\n this.#leaseProvider = getLeaseProvider(deps.pubsub);\n }\n\n async start(): Promise<void> {\n if (this.#running) return;\n if (!this.deps) throw new Error('PlatformGitLabEventWorker: call init() before start()');\n\n this.#startedAt = this.#now() - 1;\n this.#settings = normalizeSettings(await this.#storage.settings.get(CURSOR_ORG_ID, CURSOR_USER_ID));\n this.#running = true;\n this.deps.logger.info('Platform GitLab event polling started', {\n intervalMs: this.#intervalMs,\n leaseTtlMs: this.#leaseTtlMs,\n });\n this.#schedule(0);\n }\n\n async stop(): Promise<void> {\n if (!this.#running) return;\n this.#running = false;\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n this.#stopLeaseRenewal();\n await this.#inFlight;\n if (this.#hasLease) {\n await this.#leaseProvider.releaseLease(this.#leaseKey(), this.#leaseOwner).catch(() => undefined);\n this.#hasLease = false;\n }\n }\n\n get isRunning(): boolean {\n return this.#running;\n }\n\n #schedule(delayMs: number): void {\n if (!this.#running) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const run = this.#tick();\n this.#inFlight = run;\n void run.finally(() => {\n if (this.#inFlight === run) this.#inFlight = undefined;\n });\n }, delayMs);\n this.#timer.unref?.();\n }\n\n async #tick(): Promise<void> {\n let nextDelay = this.#intervalMs;\n try {\n if (!(await this.#ensureLease())) return;\n nextDelay = await this.#poll();\n } catch (error) {\n nextDelay = retryDelay(error, this.#intervalMs);\n this.deps?.logger.error('Platform GitLab event polling cycle failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: nextDelay,\n });\n } finally {\n this.#schedule(nextDelay);\n }\n }\n\n async #ensureLease(): Promise<boolean> {\n if (this.#hasLease) return true;\n const result = await this.#leaseProvider.acquireLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs);\n this.#hasLease = result.acquired;\n if (this.#hasLease) this.#startLeaseRenewal();\n return this.#hasLease;\n }\n\n #startLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) return;\n this.#leaseRenewalTimer = setInterval(\n () => {\n void this.#leaseProvider\n .renewLease(this.#leaseKey(), this.#leaseOwner, this.#leaseTtlMs)\n .then(renewed => {\n if (!renewed) {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n }\n })\n .catch(error => {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n this.deps?.logger.warn('Platform GitLab event polling lease renewal failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n Math.floor(this.#leaseTtlMs / 3),\n );\n this.#leaseRenewalTimer.unref?.();\n }\n\n #stopLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) clearInterval(this.#leaseRenewalTimer);\n this.#leaseRenewalTimer = undefined;\n }\n\n async #poll(): Promise<number> {\n const connections = (await this.#listConnections()).filter(connection => connection.status === 'active');\n let retryInMs = this.#intervalMs;\n\n for (const connection of connections) {\n if (!this.#running || !this.#hasLease) break;\n try {\n await this.#pollConnection(connection.id);\n } catch (error) {\n if (error instanceof PlatformApiError && (error.status === 401 || error.status === 404)) {\n // Platform no longer serves this connection (revoked, expired or\n // deleted). Its status will read needs_reauth on the next listing;\n // nothing to retry here, so the other connections keep flowing.\n this.deps?.logger.warn('Platform GitLab connection needs reauthorization; skipped this cycle', {\n connectionId: connection.id,\n status: error.status,\n });\n continue;\n }\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform GitLab connection event polling failed', {\n connectionId: connection.id,\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n if (error instanceof PlatformApiError && error.status === 429) break;\n }\n }\n return retryInMs;\n }\n\n async #pollConnection(connectionId: string): Promise<void> {\n if (!this.#settings.connections[connectionId]) {\n // Event ids are Redis stream ids (`<unix ms>-<sequence>`), so a\n // synthesized id at start time skips history the same way the GitHub\n // worker's start timestamp does. Events that arrived before Factory was\n // deployed are not work Factory was asked to do.\n this.#settings.connections[connectionId] = { afterEventId: `${this.#startedAt}-0` };\n await this.#saveSettings();\n }\n\n for (let pageIndex = 0; this.#running && this.#hasLease; pageIndex += 1) {\n if (pageIndex >= MAX_PAGES_PER_CONNECTION_PER_CYCLE) {\n this.deps?.logger.debug('Platform GitLab connection page budget reached; resuming next cycle', {\n connectionId,\n pages: pageIndex,\n });\n return;\n }\n const cursor: { afterEventId: string } = this.#settings.connections[connectionId]!;\n const query = new URLSearchParams({ afterEventId: cursor.afterEventId, limit: String(EVENT_PAGE_SIZE) });\n const pollStartedAt = performance.now();\n const page = await this.#client.request<{ events: IntegrationEventEntry[]; nextCursor: string | null }>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/events?${query}`,\n );\n this.deps?.logger.debug('Platform GitLab connection event poll completed', {\n connectionId,\n eventCount: page.events.length,\n latencyMs: Math.round(performance.now() - pollStartedAt),\n });\n if (page.events.length === 0 || !page.nextCursor) return;\n\n for (const event of page.events) {\n if (!this.#running || !this.#hasLease) return;\n await this.#processEvent(connectionId, event);\n }\n\n if (page.nextCursor === cursor.afterEventId) return;\n // A replica that lost its lease while processing this page must not\n // write a cursor over one the new holder has already advanced. The\n // page is replayed there; delivery is deduplicated by delivery id.\n if (!this.#hasLease) return;\n this.#settings.connections[connectionId] = { afterEventId: page.nextCursor };\n await this.#saveSettings();\n }\n }\n\n async #processEvent(connectionId: string, event: IntegrationEventEntry): Promise<void> {\n if (!event.id) {\n this.deps?.logger.warn('Platform GitLab event log returned an event without an id', { connectionId });\n return;\n }\n const deliveryId = `${DELIVERY_ID_PREFIX}:${connectionId}:${event.id}`;\n const parsed = parseGitLabWebhookBody(event.payload, deliveryId);\n if (!parsed) {\n // Pipelines, deployments, wiki edits and the like: acknowledged and\n // dropped, exactly as the direct route treats an unsupported event header.\n this.deps?.logger.debug('Platform GitLab event ignored: unsupported or malformed body', {\n connectionId,\n eventId: event.id,\n eventType: event.eventType,\n });\n return;\n }\n\n const result = await this.#process(parsed, {\n ingestFactoryEvent: this.#ingestFactoryEvent,\n controller: this.#controller,\n // The integration supplies subscription storage and the project-member\n // trust check, so the dispatcher's default author gate applies unchanged.\n gitlab: this.#gitlab,\n sourceConnectionId: connectionId,\n onConnectionMismatch: subscription => {\n // Expected whenever two connections reach the same project: the\n // event is delivered by the connection that created the subscription.\n this.deps?.logger.debug('Platform GitLab event skipped: subscription belongs to another connection', {\n deliveryId,\n subscriptionId: subscription.id,\n subscriptionConnectionId: subscription.data.installationExternalId,\n });\n },\n onTargetSkipped: subscription => {\n // Routine when a subscription's thread belongs to another deployment,\n // so this stays at debug rather than warning on a loop.\n this.deps?.logger.debug('Platform GitLab event skipped: thread is not held here', {\n deliveryId,\n subscriptionId: subscription.id,\n threadId: subscription.threadId,\n });\n },\n onSenderRejected: notification => {\n this.deps?.logger.debug('Platform GitLab event dropped: sender not authorized', {\n deliveryId,\n repository: notification.metadata.projectPath,\n sender: notification.metadata.sender,\n kind: notification.kind,\n });\n },\n onTargetError: (subscription, error) => {\n this.deps?.logger.error('Platform GitLab event delivery failed for a subscription', {\n deliveryId,\n subscriptionId: subscription.id,\n resourceId: subscription.resourceId,\n threadId: subscription.threadId,\n error: error instanceof Error ? error.message : String(error),\n });\n },\n });\n if (result.status !== 202) {\n this.deps?.logger.warn('Platform GitLab event was not accepted by the webhook processor', {\n connectionId,\n deliveryId,\n status: result.status,\n });\n }\n }\n\n async #saveSettings(): Promise<void> {\n await this.#storage.settings.save(CURSOR_ORG_ID, CURSOR_USER_ID, this.#settings);\n }\n\n #leaseKey(): string {\n return `${this.name}:${this.#storage.integrationId}`;\n }\n}\n\nfunction getLeaseProvider(pubsub: PubSub): LeaseProvider {\n const getProvider = (pubsub as PubSub & { getLeaseProvider?: () => LeaseProvider | undefined }).getLeaseProvider;\n if (typeof getProvider === 'function') return getProvider.call(pubsub) ?? NoopLeaseProvider;\n return isLeaseProvider(pubsub) ? pubsub : NoopLeaseProvider;\n}\n\nfunction normalizeSettings(value: PlatformGitLabEventWorkerSettings | null): PlatformGitLabEventWorkerSettings {\n if (!value || value.version !== 1 || !value.connections || typeof value.connections !== 'object') {\n return { version: 1, connections: {} };\n }\n const connections: PlatformGitLabEventWorkerSettings['connections'] = {};\n for (const [connectionId, cursor] of Object.entries(value.connections)) {\n if (typeof cursor?.afterEventId === 'string' && cursor.afterEventId.length > 0) {\n connections[connectionId] = { afterEventId: cursor.afterEventId };\n }\n }\n return { version: 1, connections };\n}\n\nfunction retryDelay(error: unknown, fallbackMs: number): number {\n if (error instanceof PlatformApiError && error.status === 429 && error.retryAfterSeconds !== null) {\n return Math.max(fallbackMs, error.retryAfterSeconds * 1_000);\n }\n return fallbackMs;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/jira/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAIV,MAAM,EACN,WAAW,EASZ,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAKrF,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,kBAAkB,CAAC;AAI1B,UAAU,6BAA6B;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAaD,UAAU,kBAAkB;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,MAAM,WAAW,6BAA6B;IAC5C,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,qBAAa,uBAAwB,YAAW,kBAAkB;;IAChE,QAAQ,CAAC,EAAE,UAAU;IAYrB,IAAI,KAAK,IAAI,cAAc,CAE1B;gBAEW,MAAM,GAAE,6BAAkC;IAOtD,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QAAE,QAAQ,EAAE,sBAAsB,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI;IAM3F,IAAI,QAAQ,IAAI,sBAAsB,CAIrC;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAkB9D,WAAW,IAAI,IAAI;IAMb,eAAe,IAAI,OAAO,CAAC,6BAA6B,EAAE,CAAC;IAS3D,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IAI9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAQrB;IA8EI,gBAAgB,CACpB,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,GAAE,MAAM,EAAO,EACxB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC;QACT,MAAM,EAAE,KAAK,CAAC,WAAW,GAAG;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACpF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC;
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/jira/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAIV,MAAM,EACN,WAAW,EASZ,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAKrF,OAAO,EAIL,KAAK,uBAAuB,EAC7B,MAAM,kBAAkB,CAAC;AAI1B,UAAU,6BAA6B;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAaD,UAAU,kBAAkB;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAqCD,MAAM,WAAW,6BAA6B;IAC5C,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,qBAAa,uBAAwB,YAAW,kBAAkB;;IAChE,QAAQ,CAAC,EAAE,UAAU;IAYrB,IAAI,KAAK,IAAI,cAAc,CAE1B;gBAEW,MAAM,GAAE,6BAAkC;IAOtD,UAAU,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QAAE,QAAQ,EAAE,sBAAsB,CAAC;QAAC,IAAI,EAAE,SAAS,CAAA;KAAE,GAAG,IAAI;IAM3F,IAAI,QAAQ,IAAI,sBAAsB,CAIrC;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAEK,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAkB9D,WAAW,IAAI,IAAI;IAMb,eAAe,IAAI,OAAO,CAAC,6BAA6B,EAAE,CAAC;IAS3D,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IAI9C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAQrB;IA8EI,gBAAgB,CACpB,MAAM,CAAC,EAAE,MAAM,EACf,SAAS,GAAE,MAAM,EAAO,EACxB,MAAM,CAAC,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC;QACT,MAAM,EAAE,KAAK,CAAC,WAAW,GAAG;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACpF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC;IAoOF,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,YAAY,EAAE;IAchD,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAUrC,UAAU,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAIrF,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGvC;AAWD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYnG;AAED,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,kBAAkB,GAAG,MAAM,CAE1E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAe7E"}
|
|
@@ -264,7 +264,13 @@ var PlatformJiraIntegration = class {
|
|
|
264
264
|
if (error instanceof JiraApiError && error.status === 404) return null;
|
|
265
265
|
throw error;
|
|
266
266
|
}
|
|
267
|
-
|
|
267
|
+
let comment;
|
|
268
|
+
try {
|
|
269
|
+
comment = await resolved.context.api.createComment(key, input.body);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
if (error instanceof JiraApiError && error.status === 404) return null;
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
268
274
|
return {
|
|
269
275
|
id: comment.id,
|
|
270
276
|
url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.id}`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.js","names":["#clientConfig","#platformClient","#endpointHost","#cloudIdByConnectionId","#siteUrlByConnectionId","#orgIdByResourceId","#rules","#projects","#auth","#activeConnections","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#connectionContext","#connectionContextById","#toIntakeIssue","#findIssue","#listAllComments","#resolveRequest","#cloudId"],"sources":["../../../../src/integrations/platform/jira/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { IssueReconcileWorker } from '../../issue-reconcile-worker.js';\nimport { adfToText } from '../../jira/adf.js';\nimport type { JiraComment, JiraIssue, JiraTransition } from '../../jira/api.js';\nimport { JiraApiClient, JiraApiError } from '../../jira/api.js';\nimport type { JiraEventRules, JiraRuleOverrides } from '../../jira/default-rules.js';\nimport { resolveJiraRules } from '../../jira/default-rules.js';\nimport { attachJiraIssueReconciler } from '../../jira/issue-reconciler.js';\nimport { jiraReconciliationEnabled, jiraReconciliationInterval } from '../../jira/reconciliation-config.js';\nimport { attachJiraRules } from '../../jira/rules.js';\nimport {\n logPlatformInfo,\n PlatformApiClient,\n platformApiClientConfigFromEnv,\n type PlatformApiClientConfig,\n} from '../api-client.js';\nimport { buildPlatformJiraAgentTools } from './agent-tools.js';\nimport { buildPlatformJiraRoutes } from './routes.js';\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface JiraConnectionContext {\n connection: PlatformIntegrationConnection;\n api: JiraApiClient;\n siteUrl: string;\n}\n\ninterface PlatformConnectionContextResponse {\n connection_config: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n}\n\ninterface JiraIssueReference {\n connectionId: string;\n issueId: string;\n projectId?: string;\n}\n\ninterface JiraPageCursor {\n group: number;\n jira?: string;\n}\n\nconst ISSUE_COMMENTS_MAX_PAGES = 20;\n\n/** Hard stop for project pagination so a misbehaving `isLast` can't loop forever. */\nconst PROJECT_SEARCH_MAX_PAGES = 50;\nconst ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\\d+$/;\nconst PLATFORM_JIRA_PROVIDER_CONFIG_KEY = 'jira';\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst JIRA_CONNECTION_TOKEN_PREFIX = 'jira-connection:';\nconst JIRA_ISSUE_REF_PREFIX = 'jira-issue:';\nconst JIRA_SOURCE_PREFIX = 'jira-project:';\n\nconst STATE_TYPE_TO_CATEGORY: Record<'unstarted' | 'started' | 'completed', string> = {\n unstarted: 'new',\n started: 'indeterminate',\n completed: 'done',\n};\n\nfunction stateTypeFromCategory(key: string | undefined): string | null {\n switch (key) {\n case 'new':\n return 'unstarted';\n case 'indeterminate':\n return 'started';\n case 'done':\n return 'completed';\n default:\n return null;\n }\n}\n\nexport interface PlatformJiraIntegrationConfig {\n clientConfig?: PlatformApiClientConfig;\n /** Per-event replacements; omitted events retain defaults, null disables. */\n rules?: JiraRuleOverrides;\n}\n\nexport class PlatformJiraIntegration implements FactoryIntegration {\n readonly id = 'jira';\n readonly #clientConfig: PlatformApiClientConfig;\n readonly #platformClient: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #cloudIdByConnectionId = new Map<string, string>();\n readonly #siteUrlByConnectionId = new Map<string, string>();\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n readonly #rules: JiraEventRules;\n\n get rules(): JiraEventRules {\n return this.#rules;\n }\n\n constructor(config: PlatformJiraIntegrationConfig = {}) {\n this.#rules = resolveJiraRules(config.rules);\n this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#platformClient = new PlatformApiClient(this.#clientConfig);\n this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Jira integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects)\n throw new Error('PlatformJiraIntegration is not initialized — the factory binds storage during prepare().');\n return this.#projects;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n this.#cloudIdByConnectionId.clear();\n this.#siteUrlByConnectionId.clear();\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n const providerKey = encodeURIComponent(PLATFORM_JIRA_PROVIDER_CONFIG_KEY);\n const result = await this.#platformClient.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${providerKey}`,\n );\n return result.connections;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n return {\n connection: jiraConnection(reference.connectionId),\n ...(reference.projectId ? { sourceId: encodeSourceId(reference.connectionId, reference.projectId) } : {}),\n issueId: reference.issueId,\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources: IntakeSource[] = [];\n for (const connection of await this.#activeConnections()) {\n const context = await this.#connectionContext(connection);\n let startAt = 0;\n for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {\n const page = await context.api.listProjects({ startAt });\n for (const project of page.values) {\n sources.push({\n id: encodeSourceId(connection.id, project.id),\n name: project.name,\n type: 'project',\n metadata: {\n key: project.key,\n connectionId: connection.id,\n site: new URL(context.siteUrl).host,\n },\n });\n }\n if (page.isLast || page.values.length === 0) break;\n startAt += page.values.length;\n }\n }\n return sources;\n }\n\n async #listItems({ sourceIds, cursor }: ListIntakeItemsInput): Promise<IntakeItemPage> {\n if (sourceIds.length === 0) return { items: [], nextCursor: null };\n const page = await this.listActiveIssues(cursor, sourceIds);\n return {\n items: page.issues.map(issue => ({\n source: { type: 'issue', externalId: issue.externalId, url: issue.url },\n sourceId: issue.sourceId,\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 identifier: issue.identifier,\n issueRef: issue.externalId,\n state: issue.state,\n stateType: issue.stateType,\n priority: issue.priority,\n project: issue.source,\n site: issue.site,\n assignee: issue.assignee,\n assignees: issue.assignees ?? [],\n creator: issue.author,\n author: issue.author,\n labels: issue.labels,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n },\n })),\n nextCursor: page.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput): Promise<{ issues: IntakeIssue[]; nextCursor: string | null }> {\n const page = await this.listActiveIssues(input.cursor, input.sourceIds, input.labels);\n return { issues: page.issues, nextCursor: page.nextCursor };\n }\n\n async listActiveIssues(\n cursor?: string,\n sourceIds: string[] = [],\n labels?: string[],\n ): Promise<{\n issues: Array<IntakeIssue & { sourceId: string; externalId: string; site: string }>;\n nextCursor: string | null;\n }> {\n const grouped = groupSources(sourceIds);\n if (grouped.length === 0) return { issues: [], nextCursor: null };\n const decodedCursor = decodePageCursor(cursor);\n if (decodedCursor.group >= grouped.length) return { issues: [], nextCursor: null };\n\n for (let groupIndex = decodedCursor.group; groupIndex < grouped.length; groupIndex++) {\n const group = grouped[groupIndex]!;\n const context = await this.#connectionContextById(group.connectionId);\n const jql = buildIntakeJql(group.projectIds, labels);\n if (jql === null) continue;\n const page = await context.api.searchIssues({\n jql,\n ...(groupIndex === decodedCursor.group && decodedCursor.jira ? { nextPageToken: decodedCursor.jira } : {}),\n });\n const nextCursor = page.nextPageToken\n ? encodePageCursor({ group: groupIndex, jira: page.nextPageToken })\n : groupIndex + 1 < grouped.length\n ? encodePageCursor({ group: groupIndex + 1 })\n : null;\n return {\n issues: page.issues.map(issue => {\n const projectId = issue.fields.project?.id ?? group.projectIds[0] ?? '';\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n sourceId: encodeSourceId(group.connectionId, projectId),\n externalId: encodeIssueReference({ connectionId: group.connectionId, issueId: issue.key, projectId }),\n site: new URL(context.siteUrl).host,\n };\n }),\n nextCursor,\n };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const { context, issue } = (await this.#findIssue(input)) ?? {};\n if (!context || !issue) return null;\n const { comments, total } = await this.#listAllComments(context.api, issue.key);\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n commentCount: total,\n description: issue.fields.description ? adfToText(issue.fields.description) || null : null,\n comments: comments.map(comment => ({\n author: comment.author?.displayName ?? null,\n body: adfToText(comment.body),\n createdAt: comment.created,\n })),\n };\n }\n\n async #listAllComments(api: JiraApiClient, keyOrId: string): Promise<{ comments: JiraComment[]; total: number }> {\n const comments: JiraComment[] = [];\n let total = 0;\n for (let page = 0; page < ISSUE_COMMENTS_MAX_PAGES; page++) {\n const result = await api.listComments(keyOrId, { startAt: comments.length });\n comments.push(...result.comments);\n total = result.total;\n if (result.comments.length === 0 || comments.length >= total) break;\n }\n return { comments, total };\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n let key = resolved.issueId;\n if (!ISSUE_KEY_PATTERN.test(key)) {\n try {\n key = (await resolved.context.api.getIssue(key)).key;\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n }\n const comment = await resolved.context.api.createComment(key, input.body);\n return {\n id: comment.id,\n url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.id}`,\n };\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: JiraIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.issueId);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n if (currentStatusMatches(issue, input.state)) return this.#toIntakeIssue(issue, resolved.context.siteUrl);\n const transitions = await resolved.context.api.listTransitions(issue.key);\n const transition = resolveTransition(transitions, input.state);\n if (!transition) return null;\n await resolved.context.api.applyTransition(issue.key, transition.id);\n return this.#toIntakeIssue(await resolved.context.api.getIssue(issue.key), resolved.context.siteUrl);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issueId: string }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n if (connectionId) return { context: await this.#connectionContextById(connectionId), issueId };\n const active = await this.#activeConnections();\n if (active.length !== 1) {\n throw new JiraApiError('Jira issue reference must identify a connection when multiple sites are connected.', 400);\n }\n return { context: await this.#connectionContext(active[0]!), issueId };\n }\n\n /**\n * Locate an issue for a read. A connection-scoped reference resolves\n * directly; a plain issue key (the agent tool's normal input) is looked up\n * across every active connection so multi-site organizations can still\n * fetch by key — the first site that knows the key wins, which is safe\n * because Jira project-key prefixes make cross-site collisions unlikely and\n * the tool is read-only.\n */\n async #findIssue(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issue: JiraIssue } | null> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n\n const candidates: JiraConnectionContext[] = [];\n if (connectionId) {\n candidates.push(await this.#connectionContextById(connectionId));\n } else {\n const active = await this.#activeConnections();\n if (active.length === 0) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n for (const connection of active) candidates.push(await this.#connectionContext(connection));\n }\n\n let firstError: unknown;\n for (const context of candidates) {\n try {\n return { context, issue: await context.api.getIssue(issueId) };\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) continue;\n firstError ??= error;\n }\n }\n // Not found anywhere. If a site failed for a non-404 reason, surface that\n // failure instead of a silent \"not found\".\n if (firstError !== undefined) throw firstError;\n return null;\n }\n\n #toIntakeIssue(issue: JiraIssue, siteUrl: string): IntakeIssue {\n const { fields } = issue;\n return {\n id: issue.id,\n identifier: issue.key,\n title: fields.summary ?? issue.key,\n url: `${siteUrl}/browse/${issue.key}`,\n author: fields.reporter?.displayName ?? null,\n state: fields.status?.name ?? null,\n stateType: stateTypeFromCategory(fields.status?.statusCategory?.key),\n priority: fields.priority?.name ?? null,\n assignee: fields.assignee?.displayName ?? null,\n source: fields.project?.key ?? null,\n labels: fields.labels ?? [],\n commentCount: null,\n createdAt: fields.created ?? '',\n updatedAt: fields.updated ?? '',\n };\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n return (await this.listConnections()).filter(connection => connection.status === 'active');\n }\n\n async #connectionContextById(connectionId: string): Promise<JiraConnectionContext> {\n const connection = (await this.#activeConnections()).find(candidate => candidate.id === connectionId);\n if (!connection) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n return this.#connectionContext(connection);\n }\n\n async #cloudId(connectionId: string): Promise<string> {\n const cached = this.#cloudIdByConnectionId.get(connectionId);\n if (cached) return cached;\n\n const context = await this.#platformClient.request<PlatformConnectionContextResponse>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/context`,\n );\n const cloudId = context.connection_config?.cloudId;\n if (typeof cloudId !== 'string' || !UUID_PATTERN.test(cloudId)) {\n throw new JiraApiError('Platform Jira connection context is missing a valid cloudId.', 502);\n }\n this.#cloudIdByConnectionId.set(connectionId, cloudId);\n return cloudId;\n }\n\n async #connectionContext(connection: PlatformIntegrationConnection): Promise<JiraConnectionContext> {\n const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\\/+$/, '')}/v2/connections/${encodeURIComponent(\n connection.id,\n )}/proxy`;\n const cloudId = await this.#cloudId(connection.id);\n const api = new JiraApiClient({\n baseUrl: `${proxyBaseUrl}/ex/jira/${encodeURIComponent(cloudId)}`,\n accessToken: this.#clientConfig.accessToken,\n ...(this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}),\n });\n let siteUrl = this.#siteUrlByConnectionId.get(connection.id);\n if (!siteUrl) {\n siteUrl = connection.accountLabel\n ? normalizeSiteUrl(connection.accountLabel)\n : normalizeSiteUrl((await api.getServerInfo()).baseUrl);\n this.#siteUrlByConnectionId.set(connection.id, siteUrl);\n }\n return { connection, api, siteUrl };\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!jiraReconciliationEnabled()) return [];\n const reconcile = attachJiraIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = jiraReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildPlatformJiraRoutes({\n jira: this,\n auth: ctx.auth,\n intake: ctx.storage.intake,\n appDbConfigured: Boolean(ctx.factoryStorage),\n ingestFactoryIssues: attachJiraRules(this, ctx),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildPlatformJiraAgentTools({ requestContext: args.requestContext, jira: this });\n }\n\n diagnostics(): Record<string, unknown> {\n return { configured: true, mode: 'platform', endpointHost: this.#endpointHost };\n }\n}\n\nfunction jiraConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${JIRA_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(JIRA_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(JIRA_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nexport function encodeSourceId(connectionId: string, projectId: string): string {\n return `${JIRA_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ connectionId, projectId })).toString('base64url')}`;\n}\n\nexport function decodeSourceId(sourceId: string): { connectionId: string; projectId: string } | null {\n if (!sourceId.startsWith(JIRA_SOURCE_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(sourceId.slice(JIRA_SOURCE_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n return typeof parsed.connectionId === 'string' && typeof parsed.projectId === 'string'\n ? { connectionId: parsed.connectionId, projectId: parsed.projectId }\n : null;\n } catch {\n return null;\n }\n}\n\nexport function encodeIssueReference(reference: JiraIssueReference): string {\n return `${JIRA_ISSUE_REF_PREFIX}${Buffer.from(JSON.stringify(reference)).toString('base64url')}`;\n}\n\nexport function decodeIssueReference(value: string): JiraIssueReference | null {\n if (!value.startsWith(JIRA_ISSUE_REF_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(value.slice(JIRA_ISSUE_REF_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n if (typeof parsed.connectionId !== 'string' || typeof parsed.issueId !== 'string') return null;\n return {\n connectionId: parsed.connectionId,\n issueId: parsed.issueId,\n ...(typeof parsed.projectId === 'string' ? { projectId: parsed.projectId } : {}),\n };\n } catch {\n return null;\n }\n}\n\nfunction groupSources(sourceIds: string[]): Array<{ connectionId: string; projectIds: string[] }> {\n const groups = new Map<string, Set<string>>();\n for (const sourceId of sourceIds) {\n const source = decodeSourceId(sourceId);\n if (!source) continue;\n const projectIds = groups.get(source.connectionId) ?? new Set<string>();\n projectIds.add(source.projectId);\n groups.set(source.connectionId, projectIds);\n }\n return [...groups.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([connectionId, projectIds]) => ({ connectionId, projectIds: [...projectIds].sort() }));\n}\n\nfunction encodePageCursor(cursor: JiraPageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): JiraPageCursor {\n if (!cursor) return { group: 0 };\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isInteger(parsed.group) || (parsed.group as number) < 0) throw new Error();\n return {\n group: parsed.group as number,\n ...(typeof parsed.jira === 'string' ? { jira: parsed.jira } : {}),\n };\n } catch {\n throw new JiraApiError('Jira pagination cursor is invalid.', 400);\n }\n}\n\nfunction normalizeSiteUrl(value: string): string {\n const candidate = /^https?:\\/\\//i.test(value) ? value : `https://${value}`;\n // `accountLabel` is free-form Platform data — an unparsable value must\n // surface as the classified 502 the routes already handle, not a raw\n // TypeError escaping the connection context.\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new JiraApiError('Jira site URL is invalid.', 502);\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') throw new JiraApiError('Jira site URL is invalid.', 502);\n return url.origin;\n}\n\nfunction buildIntakeJql(projectIds?: string[], labels?: string[]): string | null {\n const clauses: string[] = [];\n const safeProjects = (projectIds ?? [])\n .map(id => id.trim())\n .filter(id => /^[A-Za-z0-9_]+$/.test(id))\n .map(id => (/^\\d+$/.test(id) ? id : `\"${id}\"`));\n if (projectIds && safeProjects.length === 0) return null;\n if (safeProjects.length > 0) clauses.push(`project IN (${safeProjects.join(', ')})`);\n clauses.push('statusCategory != Done');\n const safeLabels = [...new Set((labels ?? []).map(label => label.trim().replace(/[\"\\\\]/g, '')).filter(Boolean))];\n if (safeLabels.length > 0) clauses.push(`labels IN (${safeLabels.map(label => `\"${label}\"`).join(', ')})`);\n return `${clauses.join(' AND ')} ORDER BY updated DESC`;\n}\n\nfunction currentStatusMatches(issue: JiraIssue, target: UpdateIntakeIssueInput['state']): boolean {\n const status = issue.fields.status;\n if (!status) return false;\n if (target.kind === 'byName') return status.name.toLowerCase() === target.name.toLowerCase();\n if (target.stateType === 'canceled') return status.statusCategory?.key === 'done' && /cancel/i.test(status.name);\n return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];\n}\n\nfunction resolveTransition(\n transitions: JiraTransition[],\n target: UpdateIntakeIssueInput['state'],\n): JiraTransition | null {\n if (target.kind === 'byName') {\n const wanted = target.name.toLowerCase();\n return transitions.find(transition => transition.to?.name.toLowerCase() === wanted) ?? null;\n }\n if (target.stateType === 'canceled') {\n return (\n transitions.find(\n transition => transition.to?.statusCategory?.key === 'done' && /cancel/i.test(transition.to.name),\n ) ?? null\n );\n }\n const category = STATE_TYPE_TO_CATEGORY[target.stateType];\n return transitions.find(transition => transition.to?.statusCategory?.key === category) ?? null;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#clientConfig","#platformClient","#endpointHost","#cloudIdByConnectionId","#siteUrlByConnectionId","#orgIdByResourceId","#rules","#projects","#auth","#activeConnections","#resolveIntakeDispatch","#listSources","#listItems","#listIssues","#getIssue","#createComment","#updateIssue","#connectionContext","#connectionContextById","#toIntakeIssue","#findIssue","#listAllComments","#resolveRequest","#cloudId"],"sources":["../../../../src/integrations/platform/jira/integration.ts"],"sourcesContent":["import type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute } from '@mastra/core/server';\nimport type { MastraWorker } from '@mastra/core/worker';\n\nimport type { IntegrationConnection } from '../../../capabilities/connection.js';\nimport type {\n CreateIntakeCommentInput,\n CreatedIntakeComment,\n GetIntakeIssueInput,\n Intake,\n IntakeIssue,\n IntakeIssueDetail,\n IntakeItemPage,\n IntakeSource,\n ListIntakeIssuesInput,\n ListIntakeItemsInput,\n ResolveIntakeDispatchInput,\n ResolvedIntakeDispatch,\n UpdateIntakeIssueInput,\n} from '../../../capabilities/intake.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';\nimport { IssueReconcileWorker } from '../../issue-reconcile-worker.js';\nimport { adfToText } from '../../jira/adf.js';\nimport type { JiraComment, JiraIssue, JiraTransition } from '../../jira/api.js';\nimport { JiraApiClient, JiraApiError } from '../../jira/api.js';\nimport type { JiraEventRules, JiraRuleOverrides } from '../../jira/default-rules.js';\nimport { resolveJiraRules } from '../../jira/default-rules.js';\nimport { attachJiraIssueReconciler } from '../../jira/issue-reconciler.js';\nimport { jiraReconciliationEnabled, jiraReconciliationInterval } from '../../jira/reconciliation-config.js';\nimport { attachJiraRules } from '../../jira/rules.js';\nimport {\n logPlatformInfo,\n PlatformApiClient,\n platformApiClientConfigFromEnv,\n type PlatformApiClientConfig,\n} from '../api-client.js';\nimport { buildPlatformJiraAgentTools } from './agent-tools.js';\nimport { buildPlatformJiraRoutes } from './routes.js';\n\ninterface PlatformIntegrationConnection {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n}\n\ninterface JiraConnectionContext {\n connection: PlatformIntegrationConnection;\n api: JiraApiClient;\n siteUrl: string;\n}\n\ninterface PlatformConnectionContextResponse {\n connection_config: Record<string, unknown> | null;\n metadata: Record<string, unknown> | null;\n}\n\ninterface JiraIssueReference {\n connectionId: string;\n issueId: string;\n projectId?: string;\n}\n\ninterface JiraPageCursor {\n group: number;\n jira?: string;\n}\n\nconst ISSUE_COMMENTS_MAX_PAGES = 20;\n\n/** Hard stop for project pagination so a misbehaving `isLast` can't loop forever. */\nconst PROJECT_SEARCH_MAX_PAGES = 50;\nconst ISSUE_KEY_PATTERN = /^[A-Za-z][A-Za-z0-9_]*-\\d+$/;\nconst PLATFORM_JIRA_PROVIDER_CONFIG_KEY = 'jira';\nconst UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst JIRA_CONNECTION_TOKEN_PREFIX = 'jira-connection:';\nconst JIRA_ISSUE_REF_PREFIX = 'jira-issue:';\nconst JIRA_SOURCE_PREFIX = 'jira-project:';\n\nconst STATE_TYPE_TO_CATEGORY: Record<'unstarted' | 'started' | 'completed', string> = {\n unstarted: 'new',\n started: 'indeterminate',\n completed: 'done',\n};\n\nfunction stateTypeFromCategory(key: string | undefined): string | null {\n switch (key) {\n case 'new':\n return 'unstarted';\n case 'indeterminate':\n return 'started';\n case 'done':\n return 'completed';\n default:\n return null;\n }\n}\n\nexport interface PlatformJiraIntegrationConfig {\n clientConfig?: PlatformApiClientConfig;\n /** Per-event replacements; omitted events retain defaults, null disables. */\n rules?: JiraRuleOverrides;\n}\n\nexport class PlatformJiraIntegration implements FactoryIntegration {\n readonly id = 'jira';\n readonly #clientConfig: PlatformApiClientConfig;\n readonly #platformClient: PlatformApiClient;\n readonly #endpointHost: string;\n readonly #cloudIdByConnectionId = new Map<string, string>();\n readonly #siteUrlByConnectionId = new Map<string, string>();\n #projects: FactoryProjectsStorage | undefined;\n #auth: RouteAuth | undefined;\n readonly #orgIdByResourceId = new Map<string, string | null>();\n\n readonly #rules: JiraEventRules;\n\n get rules(): JiraEventRules {\n return this.#rules;\n }\n\n constructor(config: PlatformJiraIntegrationConfig = {}) {\n this.#rules = resolveJiraRules(config.rules);\n this.#clientConfig = config.clientConfig ?? platformApiClientConfigFromEnv();\n this.#platformClient = new PlatformApiClient(this.#clientConfig);\n this.#endpointHost = new URL(this.#clientConfig.baseUrl).host;\n }\n\n initialize({ projects, auth }: { projects: FactoryProjectsStorage; auth: RouteAuth }): void {\n this.#projects = projects;\n this.#auth = auth;\n logPlatformInfo('Platform Jira integration initialized', { endpointHost: this.#endpointHost });\n }\n\n get projects(): FactoryProjectsStorage {\n if (!this.#projects)\n throw new Error('PlatformJiraIntegration is not initialized — the factory binds storage during prepare().');\n return this.#projects;\n }\n\n get authEnabled(): boolean {\n return this.#auth?.enabled() ?? false;\n }\n\n async resolveOrgId(resourceId: string): Promise<string | null> {\n const cached = this.#orgIdByResourceId.get(resourceId);\n if (cached !== undefined) return cached;\n if (!UUID_PATTERN.test(resourceId)) {\n this.#orgIdByResourceId.set(resourceId, null);\n return null;\n }\n try {\n await this.projects.ensureReady();\n const project = await this.projects.getById({ id: resourceId });\n const orgId = project?.orgId ?? null;\n this.#orgIdByResourceId.set(resourceId, orgId);\n return orgId;\n } catch {\n return null;\n }\n }\n\n clearCaches(): void {\n this.#orgIdByResourceId.clear();\n this.#cloudIdByConnectionId.clear();\n this.#siteUrlByConnectionId.clear();\n }\n\n async listConnections(): Promise<PlatformIntegrationConnection[]> {\n const providerKey = encodeURIComponent(PLATFORM_JIRA_PROVIDER_CONFIG_KEY);\n const result = await this.#platformClient.request<{ connections: PlatformIntegrationConnection[] }>(\n 'GET',\n `/v2/connections?providerKey=${providerKey}`,\n );\n return result.connections;\n }\n\n async hasActiveConnections(): Promise<boolean> {\n return (await this.#activeConnections()).length > 0;\n }\n\n readonly intake: Intake = {\n resolveIntakeDispatch: input => this.#resolveIntakeDispatch(input),\n listSources: () => this.#listSources(),\n listItems: input => this.#listItems(input),\n listIssues: input => this.#listIssues(input),\n getIssue: input => this.#getIssue(input),\n createComment: input => this.#createComment(input),\n updateIssue: input => this.#updateIssue(input),\n };\n\n async #resolveIntakeDispatch({ externalSource }: ResolveIntakeDispatchInput): Promise<ResolvedIntakeDispatch | null> {\n if (externalSource.type !== 'issue') return null;\n const reference = decodeIssueReference(externalSource.externalId);\n if (!reference) return null;\n return {\n connection: jiraConnection(reference.connectionId),\n ...(reference.projectId ? { sourceId: encodeSourceId(reference.connectionId, reference.projectId) } : {}),\n issueId: reference.issueId,\n };\n }\n\n async #listSources(): Promise<IntakeSource[]> {\n const sources: IntakeSource[] = [];\n for (const connection of await this.#activeConnections()) {\n const context = await this.#connectionContext(connection);\n let startAt = 0;\n for (let pageCount = 0; pageCount < PROJECT_SEARCH_MAX_PAGES; pageCount++) {\n const page = await context.api.listProjects({ startAt });\n for (const project of page.values) {\n sources.push({\n id: encodeSourceId(connection.id, project.id),\n name: project.name,\n type: 'project',\n metadata: {\n key: project.key,\n connectionId: connection.id,\n site: new URL(context.siteUrl).host,\n },\n });\n }\n if (page.isLast || page.values.length === 0) break;\n startAt += page.values.length;\n }\n }\n return sources;\n }\n\n async #listItems({ sourceIds, cursor }: ListIntakeItemsInput): Promise<IntakeItemPage> {\n if (sourceIds.length === 0) return { items: [], nextCursor: null };\n const page = await this.listActiveIssues(cursor, sourceIds);\n return {\n items: page.issues.map(issue => ({\n source: { type: 'issue', externalId: issue.externalId, url: issue.url },\n sourceId: issue.sourceId,\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 identifier: issue.identifier,\n issueRef: issue.externalId,\n state: issue.state,\n stateType: issue.stateType,\n priority: issue.priority,\n project: issue.source,\n site: issue.site,\n assignee: issue.assignee,\n assignees: issue.assignees ?? [],\n creator: issue.author,\n author: issue.author,\n labels: issue.labels,\n createdAt: issue.createdAt,\n updatedAt: issue.updatedAt,\n },\n })),\n nextCursor: page.nextCursor,\n };\n }\n\n async #listIssues(input: ListIntakeIssuesInput): Promise<{ issues: IntakeIssue[]; nextCursor: string | null }> {\n const page = await this.listActiveIssues(input.cursor, input.sourceIds, input.labels);\n return { issues: page.issues, nextCursor: page.nextCursor };\n }\n\n async listActiveIssues(\n cursor?: string,\n sourceIds: string[] = [],\n labels?: string[],\n ): Promise<{\n issues: Array<IntakeIssue & { sourceId: string; externalId: string; site: string }>;\n nextCursor: string | null;\n }> {\n const grouped = groupSources(sourceIds);\n if (grouped.length === 0) return { issues: [], nextCursor: null };\n const decodedCursor = decodePageCursor(cursor);\n if (decodedCursor.group >= grouped.length) return { issues: [], nextCursor: null };\n\n for (let groupIndex = decodedCursor.group; groupIndex < grouped.length; groupIndex++) {\n const group = grouped[groupIndex]!;\n const context = await this.#connectionContextById(group.connectionId);\n const jql = buildIntakeJql(group.projectIds, labels);\n if (jql === null) continue;\n const page = await context.api.searchIssues({\n jql,\n ...(groupIndex === decodedCursor.group && decodedCursor.jira ? { nextPageToken: decodedCursor.jira } : {}),\n });\n const nextCursor = page.nextPageToken\n ? encodePageCursor({ group: groupIndex, jira: page.nextPageToken })\n : groupIndex + 1 < grouped.length\n ? encodePageCursor({ group: groupIndex + 1 })\n : null;\n return {\n issues: page.issues.map(issue => {\n const projectId = issue.fields.project?.id ?? group.projectIds[0] ?? '';\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n sourceId: encodeSourceId(group.connectionId, projectId),\n externalId: encodeIssueReference({ connectionId: group.connectionId, issueId: issue.key, projectId }),\n site: new URL(context.siteUrl).host,\n };\n }),\n nextCursor,\n };\n }\n return { issues: [], nextCursor: null };\n }\n\n async #getIssue(input: GetIntakeIssueInput): Promise<IntakeIssueDetail | null> {\n const { context, issue } = (await this.#findIssue(input)) ?? {};\n if (!context || !issue) return null;\n const { comments, total } = await this.#listAllComments(context.api, issue.key);\n return {\n ...this.#toIntakeIssue(issue, context.siteUrl),\n commentCount: total,\n description: issue.fields.description ? adfToText(issue.fields.description) || null : null,\n comments: comments.map(comment => ({\n author: comment.author?.displayName ?? null,\n body: adfToText(comment.body),\n createdAt: comment.created,\n })),\n };\n }\n\n async #listAllComments(api: JiraApiClient, keyOrId: string): Promise<{ comments: JiraComment[]; total: number }> {\n const comments: JiraComment[] = [];\n let total = 0;\n for (let page = 0; page < ISSUE_COMMENTS_MAX_PAGES; page++) {\n const result = await api.listComments(keyOrId, { startAt: comments.length });\n comments.push(...result.comments);\n total = result.total;\n if (result.comments.length === 0 || comments.length >= total) break;\n }\n return { comments, total };\n }\n\n async #createComment(input: CreateIntakeCommentInput): Promise<CreatedIntakeComment | null> {\n const resolved = await this.#resolveRequest(input);\n let key = resolved.issueId;\n if (!ISSUE_KEY_PATTERN.test(key)) {\n try {\n key = (await resolved.context.api.getIssue(key)).key;\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n }\n let comment;\n try {\n comment = await resolved.context.api.createComment(key, input.body);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n return {\n id: comment.id,\n url: `${resolved.context.siteUrl}/browse/${key}?focusedCommentId=${comment.id}`,\n };\n }\n\n async #updateIssue(input: UpdateIntakeIssueInput): Promise<IntakeIssue | null> {\n const resolved = await this.#resolveRequest(input);\n let issue: JiraIssue;\n try {\n issue = await resolved.context.api.getIssue(resolved.issueId);\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) return null;\n throw error;\n }\n if (currentStatusMatches(issue, input.state)) return this.#toIntakeIssue(issue, resolved.context.siteUrl);\n const transitions = await resolved.context.api.listTransitions(issue.key);\n const transition = resolveTransition(transitions, input.state);\n if (!transition) return null;\n await resolved.context.api.applyTransition(issue.key, transition.id);\n return this.#toIntakeIssue(await resolved.context.api.getIssue(issue.key), resolved.context.siteUrl);\n }\n\n async #resolveRequest(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issueId: string }> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n if (connectionId) return { context: await this.#connectionContextById(connectionId), issueId };\n const active = await this.#activeConnections();\n if (active.length !== 1) {\n throw new JiraApiError('Jira issue reference must identify a connection when multiple sites are connected.', 400);\n }\n return { context: await this.#connectionContext(active[0]!), issueId };\n }\n\n /**\n * Locate an issue for a read. A connection-scoped reference resolves\n * directly; a plain issue key (the agent tool's normal input) is looked up\n * across every active connection so multi-site organizations can still\n * fetch by key — the first site that knows the key wins, which is safe\n * because Jira project-key prefixes make cross-site collisions unlikely and\n * the tool is read-only.\n */\n async #findIssue(input: GetIntakeIssueInput): Promise<{ context: JiraConnectionContext; issue: JiraIssue } | null> {\n const issueReference = decodeIssueReference(input.issueId);\n const source = input.sourceId ? decodeSourceId(input.sourceId) : null;\n const connectionId =\n issueReference?.connectionId ?? source?.connectionId ?? connectionIdFromConnection(input.connection);\n const issueId = issueReference?.issueId ?? input.issueId;\n\n const candidates: JiraConnectionContext[] = [];\n if (connectionId) {\n candidates.push(await this.#connectionContextById(connectionId));\n } else {\n const active = await this.#activeConnections();\n if (active.length === 0) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n for (const connection of active) candidates.push(await this.#connectionContext(connection));\n }\n\n let firstError: unknown;\n for (const context of candidates) {\n try {\n return { context, issue: await context.api.getIssue(issueId) };\n } catch (error) {\n if (error instanceof JiraApiError && error.status === 404) continue;\n firstError ??= error;\n }\n }\n // Not found anywhere. If a site failed for a non-404 reason, surface that\n // failure instead of a silent \"not found\".\n if (firstError !== undefined) throw firstError;\n return null;\n }\n\n #toIntakeIssue(issue: JiraIssue, siteUrl: string): IntakeIssue {\n const { fields } = issue;\n return {\n id: issue.id,\n identifier: issue.key,\n title: fields.summary ?? issue.key,\n url: `${siteUrl}/browse/${issue.key}`,\n author: fields.reporter?.displayName ?? null,\n state: fields.status?.name ?? null,\n stateType: stateTypeFromCategory(fields.status?.statusCategory?.key),\n priority: fields.priority?.name ?? null,\n assignee: fields.assignee?.displayName ?? null,\n source: fields.project?.key ?? null,\n labels: fields.labels ?? [],\n commentCount: null,\n createdAt: fields.created ?? '',\n updatedAt: fields.updated ?? '',\n };\n }\n\n async #activeConnections(): Promise<PlatformIntegrationConnection[]> {\n return (await this.listConnections()).filter(connection => connection.status === 'active');\n }\n\n async #connectionContextById(connectionId: string): Promise<JiraConnectionContext> {\n const connection = (await this.#activeConnections()).find(candidate => candidate.id === connectionId);\n if (!connection) {\n throw new JiraApiError('Jira connection is unavailable or requires reauthentication.', 401);\n }\n return this.#connectionContext(connection);\n }\n\n async #cloudId(connectionId: string): Promise<string> {\n const cached = this.#cloudIdByConnectionId.get(connectionId);\n if (cached) return cached;\n\n const context = await this.#platformClient.request<PlatformConnectionContextResponse>(\n 'GET',\n `/v2/connections/${encodeURIComponent(connectionId)}/context`,\n );\n const cloudId = context.connection_config?.cloudId;\n if (typeof cloudId !== 'string' || !UUID_PATTERN.test(cloudId)) {\n throw new JiraApiError('Platform Jira connection context is missing a valid cloudId.', 502);\n }\n this.#cloudIdByConnectionId.set(connectionId, cloudId);\n return cloudId;\n }\n\n async #connectionContext(connection: PlatformIntegrationConnection): Promise<JiraConnectionContext> {\n const proxyBaseUrl = `${this.#clientConfig.baseUrl.replace(/\\/+$/, '')}/v2/connections/${encodeURIComponent(\n connection.id,\n )}/proxy`;\n const cloudId = await this.#cloudId(connection.id);\n const api = new JiraApiClient({\n baseUrl: `${proxyBaseUrl}/ex/jira/${encodeURIComponent(cloudId)}`,\n accessToken: this.#clientConfig.accessToken,\n ...(this.#clientConfig.fetchImpl ? { fetchImpl: this.#clientConfig.fetchImpl } : {}),\n });\n let siteUrl = this.#siteUrlByConnectionId.get(connection.id);\n if (!siteUrl) {\n siteUrl = connection.accountLabel\n ? normalizeSiteUrl(connection.accountLabel)\n : normalizeSiteUrl((await api.getServerInfo()).baseUrl);\n this.#siteUrlByConnectionId.set(connection.id, siteUrl);\n }\n return { connection, api, siteUrl };\n }\n\n workers(ctx: IntegrationContext): MastraWorker[] {\n if (!jiraReconciliationEnabled()) return [];\n const reconcile = attachJiraIssueReconciler(this, ctx);\n if (!reconcile) return [];\n const intervalMs = jiraReconciliationInterval();\n return [\n new IssueReconcileWorker({\n integrationId: this.id,\n reconcile,\n ...(intervalMs ? { intervalMs } : {}),\n }),\n ];\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n return buildPlatformJiraRoutes({\n jira: this,\n auth: ctx.auth,\n intake: ctx.storage.intake,\n appDbConfigured: Boolean(ctx.factoryStorage),\n ingestFactoryIssues: attachJiraRules(this, ctx),\n });\n }\n\n async agentTools(args: { requestContext: RequestContext }): Promise<IntegrationTools> {\n return buildPlatformJiraAgentTools({ requestContext: args.requestContext, jira: this });\n }\n\n diagnostics(): Record<string, unknown> {\n return { configured: true, mode: 'platform', endpointHost: this.#endpointHost };\n }\n}\n\nfunction jiraConnection(connectionId: string): IntegrationConnection {\n return { type: 'oauth', accessToken: `${JIRA_CONNECTION_TOKEN_PREFIX}${connectionId}` };\n}\n\nfunction connectionIdFromConnection(connection: IntegrationConnection): string | null {\n if (connection.type !== 'oauth' || !connection.accessToken.startsWith(JIRA_CONNECTION_TOKEN_PREFIX)) return null;\n return connection.accessToken.slice(JIRA_CONNECTION_TOKEN_PREFIX.length) || null;\n}\n\nexport function encodeSourceId(connectionId: string, projectId: string): string {\n return `${JIRA_SOURCE_PREFIX}${Buffer.from(JSON.stringify({ connectionId, projectId })).toString('base64url')}`;\n}\n\nexport function decodeSourceId(sourceId: string): { connectionId: string; projectId: string } | null {\n if (!sourceId.startsWith(JIRA_SOURCE_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(sourceId.slice(JIRA_SOURCE_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n return typeof parsed.connectionId === 'string' && typeof parsed.projectId === 'string'\n ? { connectionId: parsed.connectionId, projectId: parsed.projectId }\n : null;\n } catch {\n return null;\n }\n}\n\nexport function encodeIssueReference(reference: JiraIssueReference): string {\n return `${JIRA_ISSUE_REF_PREFIX}${Buffer.from(JSON.stringify(reference)).toString('base64url')}`;\n}\n\nexport function decodeIssueReference(value: string): JiraIssueReference | null {\n if (!value.startsWith(JIRA_ISSUE_REF_PREFIX)) return null;\n try {\n const parsed = JSON.parse(\n Buffer.from(value.slice(JIRA_ISSUE_REF_PREFIX.length), 'base64url').toString('utf8'),\n ) as Record<string, unknown>;\n if (typeof parsed.connectionId !== 'string' || typeof parsed.issueId !== 'string') return null;\n return {\n connectionId: parsed.connectionId,\n issueId: parsed.issueId,\n ...(typeof parsed.projectId === 'string' ? { projectId: parsed.projectId } : {}),\n };\n } catch {\n return null;\n }\n}\n\nfunction groupSources(sourceIds: string[]): Array<{ connectionId: string; projectIds: string[] }> {\n const groups = new Map<string, Set<string>>();\n for (const sourceId of sourceIds) {\n const source = decodeSourceId(sourceId);\n if (!source) continue;\n const projectIds = groups.get(source.connectionId) ?? new Set<string>();\n projectIds.add(source.projectId);\n groups.set(source.connectionId, projectIds);\n }\n return [...groups.entries()]\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([connectionId, projectIds]) => ({ connectionId, projectIds: [...projectIds].sort() }));\n}\n\nfunction encodePageCursor(cursor: JiraPageCursor): string {\n return Buffer.from(JSON.stringify(cursor)).toString('base64url');\n}\n\nfunction decodePageCursor(cursor: string | undefined): JiraPageCursor {\n if (!cursor) return { group: 0 };\n try {\n const parsed = JSON.parse(Buffer.from(cursor, 'base64url').toString('utf8')) as Record<string, unknown>;\n if (!Number.isInteger(parsed.group) || (parsed.group as number) < 0) throw new Error();\n return {\n group: parsed.group as number,\n ...(typeof parsed.jira === 'string' ? { jira: parsed.jira } : {}),\n };\n } catch {\n throw new JiraApiError('Jira pagination cursor is invalid.', 400);\n }\n}\n\nfunction normalizeSiteUrl(value: string): string {\n const candidate = /^https?:\\/\\//i.test(value) ? value : `https://${value}`;\n // `accountLabel` is free-form Platform data — an unparsable value must\n // surface as the classified 502 the routes already handle, not a raw\n // TypeError escaping the connection context.\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new JiraApiError('Jira site URL is invalid.', 502);\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') throw new JiraApiError('Jira site URL is invalid.', 502);\n return url.origin;\n}\n\nfunction buildIntakeJql(projectIds?: string[], labels?: string[]): string | null {\n const clauses: string[] = [];\n const safeProjects = (projectIds ?? [])\n .map(id => id.trim())\n .filter(id => /^[A-Za-z0-9_]+$/.test(id))\n .map(id => (/^\\d+$/.test(id) ? id : `\"${id}\"`));\n if (projectIds && safeProjects.length === 0) return null;\n if (safeProjects.length > 0) clauses.push(`project IN (${safeProjects.join(', ')})`);\n clauses.push('statusCategory != Done');\n const safeLabels = [...new Set((labels ?? []).map(label => label.trim().replace(/[\"\\\\]/g, '')).filter(Boolean))];\n if (safeLabels.length > 0) clauses.push(`labels IN (${safeLabels.map(label => `\"${label}\"`).join(', ')})`);\n return `${clauses.join(' AND ')} ORDER BY updated DESC`;\n}\n\nfunction currentStatusMatches(issue: JiraIssue, target: UpdateIntakeIssueInput['state']): boolean {\n const status = issue.fields.status;\n if (!status) return false;\n if (target.kind === 'byName') return status.name.toLowerCase() === target.name.toLowerCase();\n if (target.stateType === 'canceled') return status.statusCategory?.key === 'done' && /cancel/i.test(status.name);\n return status.statusCategory?.key === STATE_TYPE_TO_CATEGORY[target.stateType];\n}\n\nfunction resolveTransition(\n transitions: JiraTransition[],\n target: UpdateIntakeIssueInput['state'],\n): JiraTransition | null {\n if (target.kind === 'byName') {\n const wanted = target.name.toLowerCase();\n return transitions.find(transition => transition.to?.name.toLowerCase() === wanted) ?? null;\n }\n if (target.stateType === 'canceled') {\n return (\n transitions.find(\n transition => transition.to?.statusCategory?.key === 'done' && /cancel/i.test(transition.to.name),\n ) ?? null\n );\n }\n const category = STATE_TYPE_TO_CATEGORY[target.stateType];\n return transitions.find(transition => transition.to?.statusCategory?.key === category) ?? null;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-worker.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/linear/event-worker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"event-worker.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/linear/event-worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { PlatformApiError } from "../api-client.js";
|
|
2
2
|
import { NoopLeaseProvider, isLeaseProvider } from "@mastra/core/events";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
4
3
|
import { MastraWorker } from "@mastra/core/worker";
|
|
5
4
|
//#region src/integrations/platform/linear/event-worker.ts
|
|
6
5
|
const API_PREFIX = "/v1/server/linear";
|
|
@@ -47,7 +46,7 @@ var PlatformLinearEventWorker = class extends MastraWorker {
|
|
|
47
46
|
#intervalMs;
|
|
48
47
|
#reconcileIntervalMs;
|
|
49
48
|
#now;
|
|
50
|
-
#leaseOwner = randomUUID();
|
|
49
|
+
#leaseOwner = globalThis.crypto.randomUUID();
|
|
51
50
|
#running = false;
|
|
52
51
|
#timer;
|
|
53
52
|
#leaseRenewalTimer;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-worker.js","names":["#client","#linear","#storage","#projects","#workItems","#ingestFactoryIssue","#reconcileFactoryState","#pollEventsEnabled","#intervalMs","#reconcileIntervalMs","#now","#leaseOwner","#leaseTtlMs","#leaseProvider","#running","#startedAt","#settings","#schedule","#timer","#stopLeaseRenewal","#inFlight","#hasLease","#tick","#ensureLease","#poll","#startLeaseRenewal","#leaseRenewalTimer","#pollWorkspace","#maybeReconcile","#lastReconcileAt","#saveSettings","#dispatchEvent"],"sources":["../../../../src/integrations/platform/linear/event-worker.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { isLeaseProvider, NoopLeaseProvider } from '@mastra/core/events';\nimport type { LeaseProvider, PubSub } from '@mastra/core/events';\nimport { MastraWorker } from '@mastra/core/worker';\nimport type { WorkerDeps } from '@mastra/core/worker';\n\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { WorkItemsStorage } from '../../../storage/domains/work-items/base.js';\nimport type { IssueReconciler } from '../../issue-reconciler.js';\nimport type { LinearIssueIngress, LinearRulesIngress } from '../../linear/rules.js';\nimport { PlatformApiClient, PlatformApiError } from '../api-client.js';\n\nconst API_PREFIX = '/v1/server/linear';\nconst DEFAULT_POLL_INTERVAL_MS = 20_000;\nconst DEFAULT_RECONCILE_INTERVAL_MS = 5 * 60_000;\nconst EVENT_PAGE_SIZE = 500;\nconst MIN_LEASE_TTL_MS = 30_000;\nconst CURSOR_ORG_ID = '__platform_linear_event_worker__';\nconst CURSOR_USER_ID = 'worker';\nconst LEASE_KEY = 'platform-linear-events:linear';\n\ntype EventCursor = { afterEventId: string } | { afterTimestamp: number };\ntype PlatformLinearEventWorkerSettings = {\n version: 1;\n workspaces: Record<string, EventCursor>;\n};\n\nexport type PlatformLinearEventStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n PlatformLinearEventWorkerSettings,\n Record<string, unknown>\n>;\n\n/**\n * Minimal envelope shape mirrored from `@platform/linear`. Kept opaque\n * (`data: unknown`) — every consumer demuxes by `type`.\n */\nexport interface LinearWebhookEnvelope {\n type: string;\n action: string | null;\n createdAt: string | null;\n webhookTimestamp: number | null;\n linearOrganizationId: string | null;\n oauthClientId: string | null;\n url: string | null;\n data: unknown;\n}\n\nexport interface LinearEventLogEntry {\n id: string;\n timestamp: number;\n envelope: LinearWebhookEnvelope;\n}\n\nexport interface PlatformLinearWorkspace {\n linearWorkspaceId: string;\n linearWorkspaceName?: string;\n}\n\n/**\n * Everything the worker needs to drive ingest + reconciliation without\n * pulling in the full `PlatformLinearIntegration` type (circular).\n */\nexport interface PlatformLinearEventDispatchIntegration {\n listWorkspaces(): Promise<PlatformLinearWorkspace[]>;\n}\n\nexport interface PlatformLinearEventWorkerConfig {\n client: PlatformApiClient;\n linear: PlatformLinearEventDispatchIntegration;\n storage: PlatformLinearEventStorage;\n projects: Pick<FactoryProjectsStorage, 'listAll'>;\n /**\n * Used to scope event dispatch to `(orgId, factoryProjectId)` pairs that\n * already have a work item linked to the incoming Linear issue. Without\n * this scoping, a workspace-scoped event would fan out to every project in\n * every org and materialize a triage card in each — a cross-tenant leak.\n * First-observation of a Linear issue happens through the user-authenticated\n * `/web/linear/issues?factoryProjectId=...` intake path instead.\n */\n workItems: Pick<WorkItemsStorage, 'list'>;\n /** Called with a single-issue rules ingress derived from an `Issue` webhook. */\n ingestFactoryIssue?: (input: LinearRulesIngress) => Promise<unknown>;\n reconcileFactoryState?: IssueReconciler;\n /** When false the worker skips event tailing and only runs the reconcile sweep. */\n pollEventsEnabled?: boolean;\n intervalMs?: number;\n reconcileIntervalMs?: number;\n now?: () => number;\n}\n\n/**\n * Tails the Platform Linear event stream (Redis-backed, per workspace) and\n * runs the same folded issue-reconcile sweep pattern as\n * `PlatformGithubEventWorker`. Issue events are translated into a normal\n * `LinearRulesIngress` so downstream rule dispatch is identical to the\n * polling path.\n *\n * Cursor state is persisted per workspace via the integration's generic\n * settings surface, keyed by a well-known worker identity. Cold start uses\n * `afterTimestamp: now - 1` to avoid replaying the 14-day stream backlog.\n *\n * ## Delivery semantics\n *\n * **At-most-once** per event. The cursor advances to the last event ID in a\n * page after all dispatch attempts on that page complete, regardless of\n * whether individual ingest calls threw. Ingest failures are logged, the\n * offending event is not retried, and drift is caught by the folded\n * `LinearIssueReconciler` sweep on its own cadence (default 5 minutes). This\n * matches `PlatformGithubEventWorker` and avoids poison-pill events blocking\n * the whole stream. Any consumer that needs exactly-once must idempotently\n * handle the same issue arriving via both the event path and the reconcile\n * path.\n */\nexport class PlatformLinearEventWorker extends MastraWorker {\n readonly name = 'platform-linear-events';\n\n readonly #client: PlatformApiClient;\n readonly #linear: PlatformLinearEventDispatchIntegration;\n readonly #storage: PlatformLinearEventStorage;\n readonly #projects: Pick<FactoryProjectsStorage, 'listAll'>;\n readonly #workItems: Pick<WorkItemsStorage, 'list'>;\n readonly #ingestFactoryIssue: ((input: LinearRulesIngress) => Promise<unknown>) | undefined;\n readonly #reconcileFactoryState: IssueReconciler | undefined;\n readonly #pollEventsEnabled: boolean;\n readonly #intervalMs: number;\n readonly #reconcileIntervalMs: number;\n readonly #now: () => number;\n readonly #leaseOwner = randomUUID();\n\n #running = false;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #leaseRenewalTimer: ReturnType<typeof setInterval> | undefined;\n #inFlight: Promise<void> | undefined;\n #leaseProvider: LeaseProvider = NoopLeaseProvider;\n #leaseTtlMs: number;\n #hasLease = false;\n #startedAt = 0;\n #lastReconcileAt = 0;\n #settings: PlatformLinearEventWorkerSettings = { version: 1, workspaces: {} };\n\n constructor(config: PlatformLinearEventWorkerConfig) {\n super();\n this.#client = config.client;\n this.#linear = config.linear;\n this.#storage = config.storage;\n this.#projects = config.projects;\n this.#workItems = config.workItems;\n this.#ingestFactoryIssue = config.ingestFactoryIssue;\n this.#reconcileFactoryState = config.reconcileFactoryState;\n this.#pollEventsEnabled = config.pollEventsEnabled ?? true;\n this.#intervalMs = config.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.#reconcileIntervalMs = config.reconcileIntervalMs ?? DEFAULT_RECONCILE_INTERVAL_MS;\n if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n throw new Error('Platform Linear event polling interval must be a positive number.');\n }\n if (!Number.isFinite(this.#reconcileIntervalMs) || this.#reconcileIntervalMs <= 0) {\n throw new Error('Platform Linear reconcile interval must be a positive number.');\n }\n this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);\n this.#now = config.now ?? Date.now;\n }\n\n async init(deps: WorkerDeps): Promise<void> {\n await super.init(deps);\n this.#leaseProvider = getLeaseProvider(deps.pubsub);\n }\n\n async start(): Promise<void> {\n if (this.#running) return;\n if (!this.deps) throw new Error('PlatformLinearEventWorker: call init() before start()');\n\n this.#startedAt = this.#now() - 1;\n this.#settings = normalizeSettings(await this.#storage.settings.get(CURSOR_ORG_ID, CURSOR_USER_ID));\n this.#running = true;\n this.deps.logger.info('Platform Linear event polling started', {\n intervalMs: this.#intervalMs,\n leaseTtlMs: this.#leaseTtlMs,\n });\n this.#schedule(0);\n }\n\n async stop(): Promise<void> {\n if (!this.#running) return;\n this.#running = false;\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n this.#stopLeaseRenewal();\n await this.#inFlight;\n if (this.#hasLease) {\n await this.#leaseProvider.releaseLease(LEASE_KEY, this.#leaseOwner).catch(() => undefined);\n this.#hasLease = false;\n }\n }\n\n get isRunning(): boolean {\n return this.#running;\n }\n\n #schedule(delayMs: number): void {\n if (!this.#running) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const run = this.#tick();\n this.#inFlight = run;\n void run.finally(() => {\n if (this.#inFlight === run) this.#inFlight = undefined;\n });\n }, delayMs);\n this.#timer.unref?.();\n }\n\n async #tick(): Promise<void> {\n let nextDelay = this.#intervalMs;\n try {\n if (!(await this.#ensureLease())) return;\n nextDelay = await this.#poll();\n } catch (error) {\n nextDelay = retryDelay(error, this.#intervalMs);\n this.deps?.logger.error('Platform Linear event polling cycle failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: nextDelay,\n });\n } finally {\n this.#schedule(nextDelay);\n }\n }\n\n async #ensureLease(): Promise<boolean> {\n if (this.#hasLease) return true;\n const result = await this.#leaseProvider.acquireLease(LEASE_KEY, this.#leaseOwner, this.#leaseTtlMs);\n this.#hasLease = result.acquired;\n if (this.#hasLease) this.#startLeaseRenewal();\n return this.#hasLease;\n }\n\n #startLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) return;\n this.#leaseRenewalTimer = setInterval(\n () => {\n void this.#leaseProvider\n .renewLease(LEASE_KEY, this.#leaseOwner, this.#leaseTtlMs)\n .then(renewed => {\n if (!renewed) {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n }\n })\n .catch(error => {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n this.deps?.logger.warn('Platform Linear event polling lease renewal failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n Math.floor(this.#leaseTtlMs / 3),\n );\n this.#leaseRenewalTimer.unref?.();\n }\n\n #stopLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) clearInterval(this.#leaseRenewalTimer);\n this.#leaseRenewalTimer = undefined;\n }\n\n async #poll(): Promise<number> {\n // Reconcile-only mode has no event tail to keep fresh, so tick on the\n // slower reconcile cadence instead of the polling interval.\n let retryInMs = this.#pollEventsEnabled ? this.#intervalMs : this.#reconcileIntervalMs;\n\n if (this.#pollEventsEnabled) {\n // Only list workspaces when actually tailing events: reconciliation\n // does not need the workspace list, and folding this call into\n // reconcile-only mode would let one workspace-listing outage take\n // down the reconcile sweep as well.\n let workspaces: PlatformLinearWorkspace[] = [];\n try {\n workspaces = await this.#linear.listWorkspaces();\n } catch (error) {\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform Linear workspace listing failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n }\n\n for (const workspace of workspaces) {\n if (!this.#running || !this.#hasLease) break;\n try {\n await this.#pollWorkspace(workspace.linearWorkspaceId);\n } catch (error) {\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform Linear workspace event polling failed', {\n linearWorkspaceId: workspace.linearWorkspaceId,\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n if (error instanceof PlatformApiError && error.status === 429) break;\n }\n }\n }\n\n await this.#maybeReconcile();\n\n return retryInMs;\n }\n\n async #maybeReconcile(): Promise<void> {\n if (!this.#reconcileFactoryState || !this.#running || !this.#hasLease) return;\n const now = this.#now();\n if (now - this.#lastReconcileAt < this.#reconcileIntervalMs) return;\n // Advance the clock before sweeping so a persistently failing sweep stays\n // on cadence instead of retrying every poll tick.\n this.#lastReconcileAt = now;\n const startedAt = Date.now();\n try {\n const { errors, ...counts } = await this.#reconcileFactoryState();\n const context = { ...counts, durationMs: Date.now() - startedAt };\n if (counts.failed > 0) {\n this.deps?.logger.warn('Platform Linear issue reconcile sweep completed with failures', {\n ...context,\n errors,\n });\n } else if (counts.updated > 0) {\n this.deps?.logger.info('Platform Linear issue reconcile patched stale metadata', context);\n } else {\n this.deps?.logger.debug('Platform Linear issue reconcile sweep completed', context);\n }\n } catch (error) {\n this.deps?.logger.error('Platform Linear issue reconcile failed', {\n durationMs: Date.now() - startedAt,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n async #pollWorkspace(linearWorkspaceId: string): Promise<void> {\n if (!this.#settings.workspaces[linearWorkspaceId]) {\n this.#settings.workspaces[linearWorkspaceId] = { afterTimestamp: this.#startedAt };\n await this.#saveSettings();\n }\n\n while (this.#running && this.#hasLease) {\n const cursor: EventCursor = this.#settings.workspaces[linearWorkspaceId]!;\n const query = new URLSearchParams({ limit: String(EVENT_PAGE_SIZE), type: 'Issue' });\n if ('afterEventId' in cursor) query.set('afterEventId', cursor.afterEventId);\n else query.set('after', String(cursor.afterTimestamp));\n\n const pollStartedAt = performance.now();\n const page = await this.#client.request<{ events: LinearEventLogEntry[] }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(linearWorkspaceId)}/events?${query}`,\n );\n this.deps?.logger.debug('Platform Linear workspace event poll completed', {\n linearWorkspaceId,\n eventCount: page.events.length,\n latencyMs: Math.round(performance.now() - pollStartedAt),\n });\n if (page.events.length === 0) return;\n\n const lastId = page.events[page.events.length - 1]!.id;\n\n // Resolve the project set once per page instead of once per event.\n // The event stream can return up to EVENT_PAGE_SIZE (500) events; the\n // project list rarely changes within the second or two it takes to\n // dispatch a single page, and #dispatchEvent still consults storage\n // per-project to check for a linked work item.\n const projects = await this.#projects.listAll();\n\n for (const event of page.events) {\n if (!this.#running || !this.#hasLease) return;\n await this.#dispatchEvent(linearWorkspaceId, event, projects);\n }\n\n if ('afterEventId' in cursor && cursor.afterEventId === lastId) return;\n this.#settings.workspaces[linearWorkspaceId] = { afterEventId: lastId };\n await this.#saveSettings();\n\n // Short pages mean we've caught up; wait for the next tick.\n if (page.events.length < EVENT_PAGE_SIZE) return;\n }\n }\n\n async #dispatchEvent(\n linearWorkspaceId: string,\n event: LinearEventLogEntry,\n projects: readonly { id: string; orgId: string }[],\n ): Promise<void> {\n if (event.envelope.type !== 'Issue') return;\n if (!this.#ingestFactoryIssue) return;\n const issue = parseIssueEnvelope(event.envelope);\n if (!issue) {\n this.deps?.logger.warn('Platform Linear event log returned an unusable Issue payload', {\n linearWorkspaceId,\n eventId: event.id,\n });\n return;\n }\n\n // The event stream is workspace-scoped and Platform Linear has no\n // workspace→org mapping we can trust. To avoid cross-tenant fan-out (an\n // Issue from org A materializing a triage card in org B via the default\n // `linearIssueObserved` rule), only dispatch to `(orgId, factoryProjectId)`\n // pairs that already have a persisted work item for this Linear issue.\n // First-observation of a Linear issue happens through the\n // user-authenticated `/web/linear/issues?factoryProjectId=...` intake path.\n const sourceKey = `linear:${issue.identifier}`;\n let dispatched = 0;\n for (const project of projects) {\n let items;\n try {\n items = await this.#workItems.list({ orgId: project.orgId, factoryProjectId: project.id });\n } catch (error) {\n this.deps?.logger.error('Platform Linear work-item lookup failed', {\n linearWorkspaceId,\n eventId: event.id,\n projectId: project.id,\n error: error instanceof Error ? error.message : String(error),\n });\n continue;\n }\n const linked = items.some(\n item =>\n item.externalSource?.integrationId === 'linear' &&\n item.externalSource.type === 'issue' &&\n item.externalSource.externalId === sourceKey,\n );\n if (!linked) continue;\n try {\n await this.#ingestFactoryIssue({\n orgId: project.orgId,\n userId: 'platform-linear-event-worker',\n factoryProjectId: project.id,\n issues: [issue],\n });\n dispatched += 1;\n } catch (error) {\n this.deps?.logger.error('Platform Linear issue ingest failed', {\n linearWorkspaceId,\n eventId: event.id,\n projectId: project.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n if (dispatched === 0) {\n this.deps?.logger.debug('Platform Linear Issue event had no linked work items to update', {\n linearWorkspaceId,\n eventId: event.id,\n sourceKey,\n });\n }\n }\n\n async #saveSettings(): Promise<void> {\n await this.#storage.settings.save(CURSOR_ORG_ID, CURSOR_USER_ID, this.#settings);\n }\n}\n\nfunction getLeaseProvider(pubsub: PubSub): LeaseProvider {\n const getProvider = (pubsub as PubSub & { getLeaseProvider?: () => LeaseProvider | undefined }).getLeaseProvider;\n if (typeof getProvider === 'function') return getProvider.call(pubsub) ?? NoopLeaseProvider;\n return isLeaseProvider(pubsub) ? pubsub : NoopLeaseProvider;\n}\n\nfunction normalizeSettings(value: PlatformLinearEventWorkerSettings | null): PlatformLinearEventWorkerSettings {\n if (!value || value.version !== 1 || !value.workspaces || typeof value.workspaces !== 'object') {\n return { version: 1, workspaces: {} };\n }\n return { version: 1, workspaces: { ...value.workspaces } };\n}\n\nfunction retryDelay(error: unknown, fallbackMs: number): number {\n if (error instanceof PlatformApiError && error.status === 429 && error.retryAfterSeconds !== null) {\n return Math.max(fallbackMs, error.retryAfterSeconds * 1_000);\n }\n return fallbackMs;\n}\n\n/**\n * Translate an Issue webhook envelope into the rules-ingress shape.\n * Returns undefined when the payload lacks the identifiers we need to\n * match a work item.\n */\nfunction parseIssueEnvelope(envelope: LinearWebhookEnvelope): LinearIssueIngress | undefined {\n const data = envelope.data;\n if (!data || typeof data !== 'object' || Array.isArray(data)) return undefined;\n const raw = data as Record<string, unknown>;\n const id = typeof raw.id === 'string' ? raw.id : undefined;\n const identifier = typeof raw.identifier === 'string' ? raw.identifier : undefined;\n const title = typeof raw.title === 'string' ? raw.title : undefined;\n const url = typeof raw.url === 'string' ? raw.url : (envelope.url ?? undefined);\n if (!id || !identifier || !title || !url) return undefined;\n\n const state = optionalObject(raw.state);\n const team = optionalObject(raw.team);\n const assignee = optionalObject(raw.assignee);\n const creator = optionalObject(raw.user) ?? optionalObject(raw.creator);\n const labels = Array.isArray(raw.labels)\n ? raw.labels.flatMap(label => {\n const obj = optionalObject(label);\n return obj && typeof obj.name === 'string' ? [obj.name] : [];\n })\n : [];\n\n const createdAt = typeof raw.createdAt === 'string' ? raw.createdAt : (envelope.createdAt ?? '');\n const updatedAt = typeof raw.updatedAt === 'string' ? raw.updatedAt : createdAt;\n\n return {\n id,\n identifier,\n title,\n url,\n state: (state && typeof state.name === 'string' ? state.name : null) ?? '',\n stateType: (state && typeof state.type === 'string' ? state.type : null) ?? '',\n priorityLabel: typeof raw.priorityLabel === 'string' ? raw.priorityLabel : '',\n assignee: assignee && typeof assignee.name === 'string' ? assignee.name : null,\n creator: creator && typeof creator.name === 'string' ? creator.name : null,\n team: team && typeof team.name === 'string' ? team.name : null,\n labels,\n createdAt,\n updatedAt,\n };\n}\n\nfunction optionalObject(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"event-worker.js","names":["#client","#linear","#storage","#projects","#workItems","#ingestFactoryIssue","#reconcileFactoryState","#pollEventsEnabled","#intervalMs","#reconcileIntervalMs","#now","#leaseOwner","#leaseTtlMs","#leaseProvider","#running","#startedAt","#settings","#schedule","#timer","#stopLeaseRenewal","#inFlight","#hasLease","#tick","#ensureLease","#poll","#startLeaseRenewal","#leaseRenewalTimer","#pollWorkspace","#maybeReconcile","#lastReconcileAt","#saveSettings","#dispatchEvent"],"sources":["../../../../src/integrations/platform/linear/event-worker.ts"],"sourcesContent":["import { isLeaseProvider, NoopLeaseProvider } from '@mastra/core/events';\nimport type { LeaseProvider, PubSub } from '@mastra/core/events';\nimport { MastraWorker } from '@mastra/core/worker';\nimport type { WorkerDeps } from '@mastra/core/worker';\n\nimport type { IntegrationStorageHandle } from '../../../storage/domains/integrations/base.js';\nimport type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';\nimport type { WorkItemsStorage } from '../../../storage/domains/work-items/base.js';\nimport type { IssueReconciler } from '../../issue-reconciler.js';\nimport type { LinearIssueIngress, LinearRulesIngress } from '../../linear/rules.js';\nimport { PlatformApiClient, PlatformApiError } from '../api-client.js';\n\nconst API_PREFIX = '/v1/server/linear';\nconst DEFAULT_POLL_INTERVAL_MS = 20_000;\nconst DEFAULT_RECONCILE_INTERVAL_MS = 5 * 60_000;\nconst EVENT_PAGE_SIZE = 500;\nconst MIN_LEASE_TTL_MS = 30_000;\nconst CURSOR_ORG_ID = '__platform_linear_event_worker__';\nconst CURSOR_USER_ID = 'worker';\nconst LEASE_KEY = 'platform-linear-events:linear';\n\ntype EventCursor = { afterEventId: string } | { afterTimestamp: number };\ntype PlatformLinearEventWorkerSettings = {\n version: 1;\n workspaces: Record<string, EventCursor>;\n};\n\nexport type PlatformLinearEventStorage = IntegrationStorageHandle<\n Record<string, unknown>,\n PlatformLinearEventWorkerSettings,\n Record<string, unknown>\n>;\n\n/**\n * Minimal envelope shape mirrored from `@platform/linear`. Kept opaque\n * (`data: unknown`) — every consumer demuxes by `type`.\n */\nexport interface LinearWebhookEnvelope {\n type: string;\n action: string | null;\n createdAt: string | null;\n webhookTimestamp: number | null;\n linearOrganizationId: string | null;\n oauthClientId: string | null;\n url: string | null;\n data: unknown;\n}\n\nexport interface LinearEventLogEntry {\n id: string;\n timestamp: number;\n envelope: LinearWebhookEnvelope;\n}\n\nexport interface PlatformLinearWorkspace {\n linearWorkspaceId: string;\n linearWorkspaceName?: string;\n}\n\n/**\n * Everything the worker needs to drive ingest + reconciliation without\n * pulling in the full `PlatformLinearIntegration` type (circular).\n */\nexport interface PlatformLinearEventDispatchIntegration {\n listWorkspaces(): Promise<PlatformLinearWorkspace[]>;\n}\n\nexport interface PlatformLinearEventWorkerConfig {\n client: PlatformApiClient;\n linear: PlatformLinearEventDispatchIntegration;\n storage: PlatformLinearEventStorage;\n projects: Pick<FactoryProjectsStorage, 'listAll'>;\n /**\n * Used to scope event dispatch to `(orgId, factoryProjectId)` pairs that\n * already have a work item linked to the incoming Linear issue. Without\n * this scoping, a workspace-scoped event would fan out to every project in\n * every org and materialize a triage card in each — a cross-tenant leak.\n * First-observation of a Linear issue happens through the user-authenticated\n * `/web/linear/issues?factoryProjectId=...` intake path instead.\n */\n workItems: Pick<WorkItemsStorage, 'list'>;\n /** Called with a single-issue rules ingress derived from an `Issue` webhook. */\n ingestFactoryIssue?: (input: LinearRulesIngress) => Promise<unknown>;\n reconcileFactoryState?: IssueReconciler;\n /** When false the worker skips event tailing and only runs the reconcile sweep. */\n pollEventsEnabled?: boolean;\n intervalMs?: number;\n reconcileIntervalMs?: number;\n now?: () => number;\n}\n\n/**\n * Tails the Platform Linear event stream (Redis-backed, per workspace) and\n * runs the same folded issue-reconcile sweep pattern as\n * `PlatformGithubEventWorker`. Issue events are translated into a normal\n * `LinearRulesIngress` so downstream rule dispatch is identical to the\n * polling path.\n *\n * Cursor state is persisted per workspace via the integration's generic\n * settings surface, keyed by a well-known worker identity. Cold start uses\n * `afterTimestamp: now - 1` to avoid replaying the 14-day stream backlog.\n *\n * ## Delivery semantics\n *\n * **At-most-once** per event. The cursor advances to the last event ID in a\n * page after all dispatch attempts on that page complete, regardless of\n * whether individual ingest calls threw. Ingest failures are logged, the\n * offending event is not retried, and drift is caught by the folded\n * `LinearIssueReconciler` sweep on its own cadence (default 5 minutes). This\n * matches `PlatformGithubEventWorker` and avoids poison-pill events blocking\n * the whole stream. Any consumer that needs exactly-once must idempotently\n * handle the same issue arriving via both the event path and the reconcile\n * path.\n */\nexport class PlatformLinearEventWorker extends MastraWorker {\n readonly name = 'platform-linear-events';\n\n readonly #client: PlatformApiClient;\n readonly #linear: PlatformLinearEventDispatchIntegration;\n readonly #storage: PlatformLinearEventStorage;\n readonly #projects: Pick<FactoryProjectsStorage, 'listAll'>;\n readonly #workItems: Pick<WorkItemsStorage, 'list'>;\n readonly #ingestFactoryIssue: ((input: LinearRulesIngress) => Promise<unknown>) | undefined;\n readonly #reconcileFactoryState: IssueReconciler | undefined;\n readonly #pollEventsEnabled: boolean;\n readonly #intervalMs: number;\n readonly #reconcileIntervalMs: number;\n readonly #now: () => number;\n readonly #leaseOwner = globalThis.crypto.randomUUID();\n\n #running = false;\n #timer: ReturnType<typeof setTimeout> | undefined;\n #leaseRenewalTimer: ReturnType<typeof setInterval> | undefined;\n #inFlight: Promise<void> | undefined;\n #leaseProvider: LeaseProvider = NoopLeaseProvider;\n #leaseTtlMs: number;\n #hasLease = false;\n #startedAt = 0;\n #lastReconcileAt = 0;\n #settings: PlatformLinearEventWorkerSettings = { version: 1, workspaces: {} };\n\n constructor(config: PlatformLinearEventWorkerConfig) {\n super();\n this.#client = config.client;\n this.#linear = config.linear;\n this.#storage = config.storage;\n this.#projects = config.projects;\n this.#workItems = config.workItems;\n this.#ingestFactoryIssue = config.ingestFactoryIssue;\n this.#reconcileFactoryState = config.reconcileFactoryState;\n this.#pollEventsEnabled = config.pollEventsEnabled ?? true;\n this.#intervalMs = config.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n this.#reconcileIntervalMs = config.reconcileIntervalMs ?? DEFAULT_RECONCILE_INTERVAL_MS;\n if (!Number.isFinite(this.#intervalMs) || this.#intervalMs <= 0) {\n throw new Error('Platform Linear event polling interval must be a positive number.');\n }\n if (!Number.isFinite(this.#reconcileIntervalMs) || this.#reconcileIntervalMs <= 0) {\n throw new Error('Platform Linear reconcile interval must be a positive number.');\n }\n this.#leaseTtlMs = Math.max(MIN_LEASE_TTL_MS, this.#intervalMs * 3);\n this.#now = config.now ?? Date.now;\n }\n\n async init(deps: WorkerDeps): Promise<void> {\n await super.init(deps);\n this.#leaseProvider = getLeaseProvider(deps.pubsub);\n }\n\n async start(): Promise<void> {\n if (this.#running) return;\n if (!this.deps) throw new Error('PlatformLinearEventWorker: call init() before start()');\n\n this.#startedAt = this.#now() - 1;\n this.#settings = normalizeSettings(await this.#storage.settings.get(CURSOR_ORG_ID, CURSOR_USER_ID));\n this.#running = true;\n this.deps.logger.info('Platform Linear event polling started', {\n intervalMs: this.#intervalMs,\n leaseTtlMs: this.#leaseTtlMs,\n });\n this.#schedule(0);\n }\n\n async stop(): Promise<void> {\n if (!this.#running) return;\n this.#running = false;\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n this.#stopLeaseRenewal();\n await this.#inFlight;\n if (this.#hasLease) {\n await this.#leaseProvider.releaseLease(LEASE_KEY, this.#leaseOwner).catch(() => undefined);\n this.#hasLease = false;\n }\n }\n\n get isRunning(): boolean {\n return this.#running;\n }\n\n #schedule(delayMs: number): void {\n if (!this.#running) return;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n const run = this.#tick();\n this.#inFlight = run;\n void run.finally(() => {\n if (this.#inFlight === run) this.#inFlight = undefined;\n });\n }, delayMs);\n this.#timer.unref?.();\n }\n\n async #tick(): Promise<void> {\n let nextDelay = this.#intervalMs;\n try {\n if (!(await this.#ensureLease())) return;\n nextDelay = await this.#poll();\n } catch (error) {\n nextDelay = retryDelay(error, this.#intervalMs);\n this.deps?.logger.error('Platform Linear event polling cycle failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: nextDelay,\n });\n } finally {\n this.#schedule(nextDelay);\n }\n }\n\n async #ensureLease(): Promise<boolean> {\n if (this.#hasLease) return true;\n const result = await this.#leaseProvider.acquireLease(LEASE_KEY, this.#leaseOwner, this.#leaseTtlMs);\n this.#hasLease = result.acquired;\n if (this.#hasLease) this.#startLeaseRenewal();\n return this.#hasLease;\n }\n\n #startLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) return;\n this.#leaseRenewalTimer = setInterval(\n () => {\n void this.#leaseProvider\n .renewLease(LEASE_KEY, this.#leaseOwner, this.#leaseTtlMs)\n .then(renewed => {\n if (!renewed) {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n }\n })\n .catch(error => {\n this.#hasLease = false;\n this.#stopLeaseRenewal();\n this.deps?.logger.warn('Platform Linear event polling lease renewal failed', {\n error: error instanceof Error ? error.message : String(error),\n });\n });\n },\n Math.floor(this.#leaseTtlMs / 3),\n );\n this.#leaseRenewalTimer.unref?.();\n }\n\n #stopLeaseRenewal(): void {\n if (this.#leaseRenewalTimer) clearInterval(this.#leaseRenewalTimer);\n this.#leaseRenewalTimer = undefined;\n }\n\n async #poll(): Promise<number> {\n // Reconcile-only mode has no event tail to keep fresh, so tick on the\n // slower reconcile cadence instead of the polling interval.\n let retryInMs = this.#pollEventsEnabled ? this.#intervalMs : this.#reconcileIntervalMs;\n\n if (this.#pollEventsEnabled) {\n // Only list workspaces when actually tailing events: reconciliation\n // does not need the workspace list, and folding this call into\n // reconcile-only mode would let one workspace-listing outage take\n // down the reconcile sweep as well.\n let workspaces: PlatformLinearWorkspace[] = [];\n try {\n workspaces = await this.#linear.listWorkspaces();\n } catch (error) {\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform Linear workspace listing failed', {\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n }\n\n for (const workspace of workspaces) {\n if (!this.#running || !this.#hasLease) break;\n try {\n await this.#pollWorkspace(workspace.linearWorkspaceId);\n } catch (error) {\n const delay = retryDelay(error, this.#intervalMs);\n retryInMs = Math.max(retryInMs, delay);\n this.deps?.logger.error('Platform Linear workspace event polling failed', {\n linearWorkspaceId: workspace.linearWorkspaceId,\n error: error instanceof Error ? error.message : String(error),\n retryInMs: delay,\n });\n if (error instanceof PlatformApiError && error.status === 429) break;\n }\n }\n }\n\n await this.#maybeReconcile();\n\n return retryInMs;\n }\n\n async #maybeReconcile(): Promise<void> {\n if (!this.#reconcileFactoryState || !this.#running || !this.#hasLease) return;\n const now = this.#now();\n if (now - this.#lastReconcileAt < this.#reconcileIntervalMs) return;\n // Advance the clock before sweeping so a persistently failing sweep stays\n // on cadence instead of retrying every poll tick.\n this.#lastReconcileAt = now;\n const startedAt = Date.now();\n try {\n const { errors, ...counts } = await this.#reconcileFactoryState();\n const context = { ...counts, durationMs: Date.now() - startedAt };\n if (counts.failed > 0) {\n this.deps?.logger.warn('Platform Linear issue reconcile sweep completed with failures', {\n ...context,\n errors,\n });\n } else if (counts.updated > 0) {\n this.deps?.logger.info('Platform Linear issue reconcile patched stale metadata', context);\n } else {\n this.deps?.logger.debug('Platform Linear issue reconcile sweep completed', context);\n }\n } catch (error) {\n this.deps?.logger.error('Platform Linear issue reconcile failed', {\n durationMs: Date.now() - startedAt,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n async #pollWorkspace(linearWorkspaceId: string): Promise<void> {\n if (!this.#settings.workspaces[linearWorkspaceId]) {\n this.#settings.workspaces[linearWorkspaceId] = { afterTimestamp: this.#startedAt };\n await this.#saveSettings();\n }\n\n while (this.#running && this.#hasLease) {\n const cursor: EventCursor = this.#settings.workspaces[linearWorkspaceId]!;\n const query = new URLSearchParams({ limit: String(EVENT_PAGE_SIZE), type: 'Issue' });\n if ('afterEventId' in cursor) query.set('afterEventId', cursor.afterEventId);\n else query.set('after', String(cursor.afterTimestamp));\n\n const pollStartedAt = performance.now();\n const page = await this.#client.request<{ events: LinearEventLogEntry[] }>(\n 'GET',\n `${API_PREFIX}/workspaces/${encodeURIComponent(linearWorkspaceId)}/events?${query}`,\n );\n this.deps?.logger.debug('Platform Linear workspace event poll completed', {\n linearWorkspaceId,\n eventCount: page.events.length,\n latencyMs: Math.round(performance.now() - pollStartedAt),\n });\n if (page.events.length === 0) return;\n\n const lastId = page.events[page.events.length - 1]!.id;\n\n // Resolve the project set once per page instead of once per event.\n // The event stream can return up to EVENT_PAGE_SIZE (500) events; the\n // project list rarely changes within the second or two it takes to\n // dispatch a single page, and #dispatchEvent still consults storage\n // per-project to check for a linked work item.\n const projects = await this.#projects.listAll();\n\n for (const event of page.events) {\n if (!this.#running || !this.#hasLease) return;\n await this.#dispatchEvent(linearWorkspaceId, event, projects);\n }\n\n if ('afterEventId' in cursor && cursor.afterEventId === lastId) return;\n this.#settings.workspaces[linearWorkspaceId] = { afterEventId: lastId };\n await this.#saveSettings();\n\n // Short pages mean we've caught up; wait for the next tick.\n if (page.events.length < EVENT_PAGE_SIZE) return;\n }\n }\n\n async #dispatchEvent(\n linearWorkspaceId: string,\n event: LinearEventLogEntry,\n projects: readonly { id: string; orgId: string }[],\n ): Promise<void> {\n if (event.envelope.type !== 'Issue') return;\n if (!this.#ingestFactoryIssue) return;\n const issue = parseIssueEnvelope(event.envelope);\n if (!issue) {\n this.deps?.logger.warn('Platform Linear event log returned an unusable Issue payload', {\n linearWorkspaceId,\n eventId: event.id,\n });\n return;\n }\n\n // The event stream is workspace-scoped and Platform Linear has no\n // workspace→org mapping we can trust. To avoid cross-tenant fan-out (an\n // Issue from org A materializing a triage card in org B via the default\n // `linearIssueObserved` rule), only dispatch to `(orgId, factoryProjectId)`\n // pairs that already have a persisted work item for this Linear issue.\n // First-observation of a Linear issue happens through the\n // user-authenticated `/web/linear/issues?factoryProjectId=...` intake path.\n const sourceKey = `linear:${issue.identifier}`;\n let dispatched = 0;\n for (const project of projects) {\n let items;\n try {\n items = await this.#workItems.list({ orgId: project.orgId, factoryProjectId: project.id });\n } catch (error) {\n this.deps?.logger.error('Platform Linear work-item lookup failed', {\n linearWorkspaceId,\n eventId: event.id,\n projectId: project.id,\n error: error instanceof Error ? error.message : String(error),\n });\n continue;\n }\n const linked = items.some(\n item =>\n item.externalSource?.integrationId === 'linear' &&\n item.externalSource.type === 'issue' &&\n item.externalSource.externalId === sourceKey,\n );\n if (!linked) continue;\n try {\n await this.#ingestFactoryIssue({\n orgId: project.orgId,\n userId: 'platform-linear-event-worker',\n factoryProjectId: project.id,\n issues: [issue],\n });\n dispatched += 1;\n } catch (error) {\n this.deps?.logger.error('Platform Linear issue ingest failed', {\n linearWorkspaceId,\n eventId: event.id,\n projectId: project.id,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n if (dispatched === 0) {\n this.deps?.logger.debug('Platform Linear Issue event had no linked work items to update', {\n linearWorkspaceId,\n eventId: event.id,\n sourceKey,\n });\n }\n }\n\n async #saveSettings(): Promise<void> {\n await this.#storage.settings.save(CURSOR_ORG_ID, CURSOR_USER_ID, this.#settings);\n }\n}\n\nfunction getLeaseProvider(pubsub: PubSub): LeaseProvider {\n const getProvider = (pubsub as PubSub & { getLeaseProvider?: () => LeaseProvider | undefined }).getLeaseProvider;\n if (typeof getProvider === 'function') return getProvider.call(pubsub) ?? NoopLeaseProvider;\n return isLeaseProvider(pubsub) ? pubsub : NoopLeaseProvider;\n}\n\nfunction normalizeSettings(value: PlatformLinearEventWorkerSettings | null): PlatformLinearEventWorkerSettings {\n if (!value || value.version !== 1 || !value.workspaces || typeof value.workspaces !== 'object') {\n return { version: 1, workspaces: {} };\n }\n return { version: 1, workspaces: { ...value.workspaces } };\n}\n\nfunction retryDelay(error: unknown, fallbackMs: number): number {\n if (error instanceof PlatformApiError && error.status === 429 && error.retryAfterSeconds !== null) {\n return Math.max(fallbackMs, error.retryAfterSeconds * 1_000);\n }\n return fallbackMs;\n}\n\n/**\n * Translate an Issue webhook envelope into the rules-ingress shape.\n * Returns undefined when the payload lacks the identifiers we need to\n * match a work item.\n */\nfunction parseIssueEnvelope(envelope: LinearWebhookEnvelope): LinearIssueIngress | undefined {\n const data = envelope.data;\n if (!data || typeof data !== 'object' || Array.isArray(data)) return undefined;\n const raw = data as Record<string, unknown>;\n const id = typeof raw.id === 'string' ? raw.id : undefined;\n const identifier = typeof raw.identifier === 'string' ? raw.identifier : undefined;\n const title = typeof raw.title === 'string' ? raw.title : undefined;\n const url = typeof raw.url === 'string' ? raw.url : (envelope.url ?? undefined);\n if (!id || !identifier || !title || !url) return undefined;\n\n const state = optionalObject(raw.state);\n const team = optionalObject(raw.team);\n const assignee = optionalObject(raw.assignee);\n const creator = optionalObject(raw.user) ?? optionalObject(raw.creator);\n const labels = Array.isArray(raw.labels)\n ? raw.labels.flatMap(label => {\n const obj = optionalObject(label);\n return obj && typeof obj.name === 'string' ? [obj.name] : [];\n })\n : [];\n\n const createdAt = typeof raw.createdAt === 'string' ? raw.createdAt : (envelope.createdAt ?? '');\n const updatedAt = typeof raw.updatedAt === 'string' ? raw.updatedAt : createdAt;\n\n return {\n id,\n identifier,\n title,\n url,\n state: (state && typeof state.name === 'string' ? state.name : null) ?? '',\n stateType: (state && typeof state.type === 'string' ? state.type : null) ?? '',\n priorityLabel: typeof raw.priorityLabel === 'string' ? raw.priorityLabel : '',\n assignee: assignee && typeof assignee.name === 'string' ? assignee.name : null,\n creator: creator && typeof creator.name === 'string' ? creator.name : null,\n team: team && typeof team.name === 'string' ? team.name : null,\n labels,\n createdAt,\n updatedAt,\n };\n}\n\nfunction optionalObject(value: unknown): Record<string, unknown> | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/linear/integration.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/linear/integration.ts"],"names":[],"mappings":"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"}
|