@pipeline-builder/api-core 5.2.2 → 5.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/lib/constants/ai-providers.d.ts +3 -22
  2. package/lib/constants/ai-providers.js +6 -56
  3. package/lib/errors/app-errors.d.ts +14 -0
  4. package/lib/errors/app-errors.js +21 -1
  5. package/lib/helpers/index.d.ts +1 -0
  6. package/lib/helpers/index.js +2 -1
  7. package/lib/helpers/sse-helpers.d.ts +33 -3
  8. package/lib/helpers/sse-helpers.js +91 -19
  9. package/lib/helpers/support-calendar.d.ts +69 -0
  10. package/lib/helpers/support-calendar.js +179 -0
  11. package/lib/middleware/auth.js +6 -3
  12. package/lib/middleware/deferred-quota-check.d.ts +5 -0
  13. package/lib/middleware/deferred-quota-check.js +33 -0
  14. package/lib/middleware/ecosystem-guard.d.ts +3 -3
  15. package/lib/middleware/ecosystem-guard.js +5 -5
  16. package/lib/middleware/index.d.ts +5 -2
  17. package/lib/middleware/index.js +6 -3
  18. package/lib/middleware/on-behalf.d.ts +37 -0
  19. package/lib/middleware/on-behalf.js +143 -0
  20. package/lib/middleware/permission-gates.d.ts +11 -0
  21. package/lib/middleware/permission-gates.js +7 -1
  22. package/lib/middleware/quota-exempt-routes.d.ts +36 -0
  23. package/lib/middleware/quota-exempt-routes.js +47 -0
  24. package/lib/middleware/route-table.d.ts +104 -0
  25. package/lib/middleware/route-table.js +72 -2
  26. package/lib/middleware/service-tokens.d.ts +8 -0
  27. package/lib/middleware/service-tokens.js +5 -3
  28. package/lib/middleware/step-up.js +11 -1
  29. package/lib/services/ai-call-weight.d.ts +24 -0
  30. package/lib/services/ai-call-weight.js +77 -0
  31. package/lib/services/alert-delivery.d.ts +99 -0
  32. package/lib/services/alert-delivery.js +132 -0
  33. package/lib/services/audit-context.d.ts +105 -0
  34. package/lib/services/audit-context.js +134 -0
  35. package/lib/services/index.d.ts +10 -2
  36. package/lib/services/index.js +10 -2
  37. package/lib/services/org-ai-client.d.ts +61 -0
  38. package/lib/services/org-ai-client.js +112 -0
  39. package/lib/services/pagerduty-inbound.d.ts +101 -0
  40. package/lib/services/pagerduty-inbound.js +195 -0
  41. package/lib/services/pagerduty.d.ts +109 -0
  42. package/lib/services/pagerduty.js +186 -0
  43. package/lib/services/quota-increment-batcher.d.ts +81 -0
  44. package/lib/services/quota-increment-batcher.js +166 -0
  45. package/lib/services/quota.d.ts +158 -14
  46. package/lib/services/quota.js +252 -22
  47. package/lib/services/remote-audit-client.d.ts +44 -1
  48. package/lib/services/remote-audit-client.js +106 -20
  49. package/lib/services/version-vuln-policy.d.ts +174 -0
  50. package/lib/services/version-vuln-policy.js +242 -0
  51. package/lib/types/ai-catalog.d.ts +63 -0
  52. package/lib/types/ai-catalog.js +82 -0
  53. package/lib/types/ask-proposals.js +6 -6
  54. package/lib/types/build-catalog.d.ts +154 -0
  55. package/lib/types/build-catalog.js +158 -0
  56. package/lib/types/common.d.ts +37 -3
  57. package/lib/types/common.js +20 -2
  58. package/lib/types/ecosystem-notifications.d.ts +5 -5
  59. package/lib/types/ecosystem-notifications.js +9 -3
  60. package/lib/types/error-codes.d.ts +13 -6
  61. package/lib/types/error-codes.js +18 -8
  62. package/lib/types/feature-flags.d.ts +71 -1
  63. package/lib/types/feature-flags.js +131 -11
  64. package/lib/types/index.d.ts +6 -4
  65. package/lib/types/index.js +12 -5
  66. package/lib/types/metadata-keys.d.ts +6 -0
  67. package/lib/types/metadata-keys.js +4 -1
  68. package/lib/types/permissions.d.ts +351 -3
  69. package/lib/types/permissions.js +610 -21
  70. package/lib/types/pipeline.d.ts +39 -0
  71. package/lib/types/pipeline.js +57 -1
  72. package/lib/types/plugin-health.d.ts +0 -2
  73. package/lib/types/plugin-health.js +1 -11
  74. package/lib/types/plugin-scan.d.ts +192 -42
  75. package/lib/types/plugin-scan.js +159 -43
  76. package/lib/types/quota-catalog.d.ts +247 -0
  77. package/lib/types/quota-catalog.js +122 -0
  78. package/lib/types/quota-tiers.d.ts +150 -0
  79. package/lib/types/quota-tiers.js +167 -2
  80. package/lib/types/remote-audit-actions.d.ts +1 -1
  81. package/lib/types/remote-audit-actions.js +217 -4
  82. package/lib/types/wire-vocabulary.js +8 -1
  83. package/lib/utils/index.d.ts +1 -1
  84. package/lib/utils/index.js +2 -2
  85. package/lib/utils/response.d.ts +35 -2
  86. package/lib/utils/response.js +42 -3
  87. package/lib/validation/ai-schemas.d.ts +27 -0
  88. package/lib/validation/ai-schemas.js +31 -5
  89. package/lib/validation/plugin-schemas.d.ts +15 -0
  90. package/lib/validation/plugin-schemas.js +28 -1
  91. package/package.json +31 -2
@@ -3,6 +3,7 @@ export { createCacheService, type RedisCacheClient, type CacheInvalidationMessag
3
3
  export { withLeaderLock, createEnvRedisLock, leaderLockKey, DEFAULT_LEADER_LOCK_TTL_MS, type LockRedis, type LeaderLockRun, type LeaderLockOptions, type LeaderLockAbortReason, } from './leader-lock.js';
4
4
  export { sendSystemNotification, SYSTEM_NOTIFY_PATH, type SystemNotification, } from './system-notification.js';
5
5
  export { createScheduler, type SchedulerOptions, type Scheduler, } from './scheduler.js';
6
+ export { assessVersionVulns, versionScanFacts, scanFreshness, scanWarnings, lastScannedAt, scanStaleHours, severityOf, maxSeverity, severityAtLeast, VULN_SEVERITIES, setVulnPolicyComplianceClientForTests, type VulnSeverity, type VulnWarning, type ScannedRecord, type VulnConsumptionPolicy, type VulnAcceptance, type VersionScanFacts, type VersionVulnInput, type VersionVulnAssessment, } from './version-vuln-policy.js';
6
7
  export { createComplianceClient, type ComplianceCheckResult, type ComplianceViolation, } from './compliance-client.js';
7
8
  export { pluginRunsOwnImage, pluginComplianceTags, derivePluginImageCompliance, type PluginComplianceAttributes, PLUGIN_IMAGE_COMPLIANCE_FIELDS, type PluginImageRow, } from '../utils/plugin-compliance.js';
8
9
  export * from './compliance-event-subscriber.js';
@@ -15,12 +16,19 @@ export { createEnvRedisDurableEventBus, type EventSubscription, type DurableEven
15
16
  export { getErrorRetryDecision, } from './retry-strategy.js';
16
17
  export type { RetryConfig, } from './retry-strategy.js';
17
18
  export { nextBackoffMs, retryForever, } from './retry-forever.js';
18
- export { getQuotaServiceAuthHeader, createQuotaService, incrementQuota, reserveQuota, sendQuotaReserveDenied, decrementQuota, type QuotaReserveResult, type QuotaService, } from './quota.js';
19
- export { createRemoteAuditClient, wireAuthzDenialAuditor, recordAudit, type RemoteAuditEvent, type RemoteAuditClient, } from './remote-audit-client.js';
19
+ export { getQuotaServiceAuthHeader, createQuotaService, incrementQuota, reserveQuota, sendQuotaReserveDenied, decrementQuota, meteredQuotaTypeFor, callQuotaExemption, QUOTA_CHECK_TIMEOUT_MS, quotaAttributionFromRequest, type AiKeySource, type AiLimitCode, type CallQuotaExemption, type QuotaReserveResult, type QuotaTokenUsage, type QuotaService, type QuotaUsageAttribution, } from './quota.js';
20
+ export { aiCallWeight, parseAiModelWeights, MAX_AI_CALL_WEIGHT, } from './ai-call-weight.js';
21
+ export { QuotaIncrementBatcher, flushQuotaIncrements, QUOTA_INCREMENT_FLUSH_MS, QUOTA_INCREMENT_BUFFER_MAX, MAX_GROUPS_PER_INCREMENT, type MeteredIncrementGroup, } from './quota-increment-batcher.js';
22
+ export { createRemoteAuditClient, wireAuthzDenialAuditor, recordAudit, type RemoteAuditEvent, type RemoteAuditWireEvent, type RemoteAuditClient, } from './remote-audit-client.js';
23
+ export { runWithAuditContext, currentAuditContext, auditContextFromRequest, auditAuthBlock, sanitizeCorrelationId, sanitizeUserAgent, AUDIT_CORRELATION_HEADERS, type AuditContext, type AuditCorrelation, type AuditJobContext, type AuditAuthBlock, } from './audit-context.js';
20
24
  export { auditSpoolKey, createEnvRedisAuditSpool, type AuditSpool, } from './audit-spool.js';
21
25
  export { resolveRedisConnection, describeRedisConnection, createRedisClient, createEnvRedisClient, createRedisReadyGate, incrWindow, RedisConfigError, type RedisEvalClient, } from './env-redis.js';
22
26
  export { createRedisTokenRevocationStore, createEnvRedisTokenRevocationStore, publishTokenRevocation, publishSessionRevocation, publishCredentialRevocation, } from './token-revocation.js';
23
27
  export { createMemorySseTicketStore, createEnvSseTicketStore, envSseTicketCaps, type EnvSseTicketStoreConfig, type SseTicketStore, type SseTicketStoreConfig, } from './sse-ticket-store.js';
24
28
  export { wireServiceSecurity, } from './service-boot.js';
25
29
  export { createWebhookChannel, createEmailChannel, createChannelRegistry, type NotificationPriority, type NotificationMessage, type ChannelTarget, type DeliveryResult, type NotificationChannel, } from './notification-channels.js';
30
+ export { alertDeliveryConfig, canDeliverAnywhere, deadmanConfig, reportAlertDeliveryPosture, slackAlertsConfig, startDeadmanHeartbeat, stopDeadmanHeartbeat, type DeadmanConfig, type AlertDeliveryConfig, type SlackAlertsConfig, } from './alert-delivery.js';
31
+ export { verifyPagerDutySignature, onCallInboundConfig, fetchRotaCoverage, parseOnCallWebhook, ackLatencySeconds, PAGERDUTY_API_URL, type OnCallInboundConfig, type OnCallWebhookEvent, type OnCallWebhookEventType, type RotaCoverage, } from './pagerduty-inbound.js';
32
+ export { sendPagerDutyEvent, pagerDutyConfig, resolveEventsEndpoint, PAGERDUTY_EVENTS_URL, type OnCallProvider, type PagerDutyAction, type PagerDutyConfig, type PagerDutyEvent, type PagerDutyResult, type PagerDutySeverity, } from './pagerduty.js';
26
33
  export { createEcosystemNotifyClient, ECOSYSTEM_NOTIFY_PATH, type EcosystemNotifyClient, } from './ecosystem-notify-client.js';
34
+ export { fetchOrgAIAccess, fetchOrgAIKey, assertOrgAIEnabled, requireOrgAIEnabled, resetOrgAICache, ORG_AI_ACCESS_PATH, ORG_AI_KEYS_PATH, type OrgAIAccess, } from './org-ai-client.js';
@@ -5,6 +5,7 @@ export { createCacheService, CacheService, } from './cache-service.js';
5
5
  export { withLeaderLock, createEnvRedisLock, leaderLockKey, DEFAULT_LEADER_LOCK_TTL_MS, } from './leader-lock.js';
6
6
  export { sendSystemNotification, SYSTEM_NOTIFY_PATH, } from './system-notification.js';
7
7
  export { createScheduler, } from './scheduler.js';
8
+ export { assessVersionVulns, versionScanFacts, scanFreshness, scanWarnings, lastScannedAt, scanStaleHours, severityOf, maxSeverity, severityAtLeast, VULN_SEVERITIES, setVulnPolicyComplianceClientForTests, } from './version-vuln-policy.js';
8
9
  export { createComplianceClient, } from './compliance-client.js';
9
10
  export { pluginRunsOwnImage, pluginComplianceTags, derivePluginImageCompliance, PLUGIN_IMAGE_COMPLIANCE_FIELDS, } from '../utils/plugin-compliance.js';
10
11
  export * from './compliance-event-subscriber.js';
@@ -22,13 +23,20 @@ export { resetCircuitBreakers, } from './circuit-breaker.js';
22
23
  export { createEnvRedisDurableEventBus, } from './durable-event-bus.js';
23
24
  export { getErrorRetryDecision, } from './retry-strategy.js';
24
25
  export { nextBackoffMs, retryForever, } from './retry-forever.js';
25
- export { getQuotaServiceAuthHeader, createQuotaService, incrementQuota, reserveQuota, sendQuotaReserveDenied, decrementQuota, } from './quota.js';
26
+ export { getQuotaServiceAuthHeader, createQuotaService, incrementQuota, reserveQuota, sendQuotaReserveDenied, decrementQuota, meteredQuotaTypeFor, callQuotaExemption, QUOTA_CHECK_TIMEOUT_MS, quotaAttributionFromRequest, } from './quota.js';
27
+ export { aiCallWeight, parseAiModelWeights, MAX_AI_CALL_WEIGHT, } from './ai-call-weight.js';
28
+ export { QuotaIncrementBatcher, flushQuotaIncrements, QUOTA_INCREMENT_FLUSH_MS, QUOTA_INCREMENT_BUFFER_MAX, MAX_GROUPS_PER_INCREMENT, } from './quota-increment-batcher.js';
26
29
  export { createRemoteAuditClient, wireAuthzDenialAuditor, recordAudit, } from './remote-audit-client.js';
30
+ export { runWithAuditContext, currentAuditContext, auditContextFromRequest, auditAuthBlock, sanitizeCorrelationId, sanitizeUserAgent, AUDIT_CORRELATION_HEADERS, } from './audit-context.js';
27
31
  export { auditSpoolKey, createEnvRedisAuditSpool, } from './audit-spool.js';
28
32
  export { resolveRedisConnection, describeRedisConnection, createRedisClient, createEnvRedisClient, createRedisReadyGate, incrWindow, RedisConfigError, } from './env-redis.js';
29
33
  export { createRedisTokenRevocationStore, createEnvRedisTokenRevocationStore, publishTokenRevocation, publishSessionRevocation, publishCredentialRevocation, } from './token-revocation.js';
30
34
  export { createMemorySseTicketStore, createEnvSseTicketStore, envSseTicketCaps, } from './sse-ticket-store.js';
31
35
  export { wireServiceSecurity, } from './service-boot.js';
32
36
  export { createWebhookChannel, createEmailChannel, createChannelRegistry, } from './notification-channels.js';
37
+ export { alertDeliveryConfig, canDeliverAnywhere, deadmanConfig, reportAlertDeliveryPosture, slackAlertsConfig, startDeadmanHeartbeat, stopDeadmanHeartbeat, } from './alert-delivery.js';
38
+ export { verifyPagerDutySignature, onCallInboundConfig, fetchRotaCoverage, parseOnCallWebhook, ackLatencySeconds, PAGERDUTY_API_URL, } from './pagerduty-inbound.js';
39
+ export { sendPagerDutyEvent, pagerDutyConfig, resolveEventsEndpoint, PAGERDUTY_EVENTS_URL, } from './pagerduty.js';
33
40
  export { createEcosystemNotifyClient, ECOSYSTEM_NOTIFY_PATH, } from './ecosystem-notify-client.js';
34
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQ0wsY0FBYyxFQUNkLHdCQUF3QixFQUN4QixtQkFBbUIsRUFDbkIsOEJBQThCLEdBQy9CLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUNMLGtCQUFrQixFQUtsQixZQUFZLEdBQ2IsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsY0FBYyxFQUNkLGtCQUFrQixFQUNsQixhQUFhLEVBQ2IsMEJBQTBCLEdBSzNCLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixrQkFBa0IsR0FFbkIsTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEVBQ0wsZUFBZSxHQUdoQixNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFDTCxzQkFBc0IsR0FHdkIsTUFBTSx3QkFBd0IsQ0FBQztBQUNoQyxPQUFPLEVBQ0wsa0JBQWtCLEVBQ2xCLG9CQUFvQixFQUNwQiwyQkFBMkIsRUFFM0IsOEJBQThCLEdBRS9CLE1BQU0sK0JBQStCLENBQUM7QUFDdkMsY0FBYyxrQ0FBa0MsQ0FBQztBQUNqRCxPQUFPLEVBR0wsWUFBWSxHQUNiLE1BQU0sb0JBQW9CLENBQUM7QUFDNUIsT0FBTyxFQUNMLHVCQUF1QixFQUN2QixnQkFBZ0IsRUFDaEIscUJBQXFCLEVBQ3JCLGtCQUFrQixHQUNuQixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFDTCxvQkFBb0IsRUFDcEIsb0JBQW9CLEVBQ3BCLGVBQWUsRUFDZixTQUFTLEdBQ1YsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QixpRkFBaUY7QUFDakYsOEVBQThFO0FBQzlFLGlGQUFpRjtBQUNqRiwrREFBK0Q7QUFDL0QsT0FBTyxFQUNMLFlBQVksRUFDWixlQUFlLEVBQ2YsZ0JBQWdCLEdBQ2pCLE1BQU0sbUJBQW1CLENBQUM7QUFDM0IsK0VBQStFO0FBQy9FLHlEQUF5RDtBQUN6RCxPQUFPLEVBQ0wsb0JBQW9CLEdBQ3JCLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUNMLDZCQUE2QixHQUc5QixNQUFNLHdCQUF3QixDQUFDO0FBQ2hDLE9BQU8sRUFDTCxxQkFBcUIsR0FDdEIsTUFBTSxxQkFBcUIsQ0FBQztBQUk3QixPQUFPLEVBQ0wsYUFBYSxFQUNiLFlBQVksR0FDYixNQUFNLG9CQUFvQixDQUFDO0FBQzVCLE9BQU8sRUFDTCx5QkFBeUIsRUFDekIsa0JBQWtCLEVBQ2xCLGNBQWMsRUFDZCxZQUFZLEVBQ1osc0JBQXNCLEVBQ3RCLGNBQWMsR0FHZixNQUFNLFlBQVksQ0FBQztBQUNwQixPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLHNCQUFzQixFQUN0QixXQUFXLEdBR1osTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEVBQ0wsYUFBYSxFQUNiLHdCQUF3QixHQUV6QixNQUFNLGtCQUFrQixDQUFDO0FBQzFCLE9BQU8sRUFDTCxzQkFBc0IsRUFDdEIsdUJBQXVCLEVBQ3ZCLGlCQUFpQixFQUNqQixvQkFBb0IsRUFDcEIsb0JBQW9CLEVBQ3BCLFVBQVUsRUFDVixnQkFBZ0IsR0FFakIsTUFBTSxnQkFBZ0IsQ0FBQztBQUN4QixPQUFPLEVBQ0wsK0JBQStCLEVBQy9CLGtDQUFrQyxFQUNsQyxzQkFBc0IsRUFDdEIsd0JBQXdCLEVBQ3hCLDJCQUEyQixHQUM1QixNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFDTCwwQkFBMEIsRUFDMUIsdUJBQXVCLEVBQ3ZCLGdCQUFnQixHQUlqQixNQUFNLHVCQUF1QixDQUFDO0FBQy9CLE9BQU8sRUFDTCxtQkFBbUIsR0FDcEIsTUFBTSxtQkFBbUIsQ0FBQztBQUMzQixPQUFPLEVBQ0wsb0JBQW9CLEVBQ3BCLGtCQUFrQixFQUNsQixxQkFBcUIsR0FNdEIsTUFBTSw0QkFBNEIsQ0FBQztBQUNwQyxPQUFPLEVBQ0wsMkJBQTJCLEVBQzNCLHFCQUFxQixHQUV0QixNQUFNLDhCQUE4QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmV4cG9ydCB7XG4gIGV4Y2hhbmdlQXBpS2V5LFxuICByZXNldEFwaUtleUV4Y2hhbmdlQ2FjaGUsXG4gIEFwaUtleVJlamVjdGVkRXJyb3IsXG4gIEFwaUtleUV4Y2hhbmdlVW5hdmFpbGFibGVFcnJvcixcbn0gZnJvbSAnLi9hcGkta2V5LWV4Y2hhbmdlLmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZUNhY2hlU2VydmljZSxcbiAgdHlwZSBSZWRpc0NhY2hlQ2xpZW50LFxuICB0eXBlIENhY2hlSW52YWxpZGF0aW9uTWVzc2FnZSxcbiAgdHlwZSBDYWNoZUludmFsaWRhdGlvbkJ1cyxcbiAgdHlwZSBSZWRpc0ludmFsaWRhdGlvbkNsaWVudCxcbiAgQ2FjaGVTZXJ2aWNlLFxufSBmcm9tICcuL2NhY2hlLXNlcnZpY2UuanMnO1xuZXhwb3J0IHtcbiAgd2l0aExlYWRlckxvY2ssXG4gIGNyZWF0ZUVudlJlZGlzTG9jayxcbiAgbGVhZGVyTG9ja0tleSxcbiAgREVGQVVMVF9MRUFERVJfTE9DS19UVExfTVMsXG4gIHR5cGUgTG9ja1JlZGlzLFxuICB0eXBlIExlYWRlckxvY2tSdW4sXG4gIHR5cGUgTGVhZGVyTG9ja09wdGlvbnMsXG4gIHR5cGUgTGVhZGVyTG9ja0Fib3J0UmVhc29uLFxufSBmcm9tICcuL2xlYWRlci1sb2NrLmpzJztcbmV4cG9ydCB7XG4gIHNlbmRTeXN0ZW1Ob3RpZmljYXRpb24sXG4gIFNZU1RFTV9OT1RJRllfUEFUSCxcbiAgdHlwZSBTeXN0ZW1Ob3RpZmljYXRpb24sXG59IGZyb20gJy4vc3lzdGVtLW5vdGlmaWNhdGlvbi5qcyc7XG5leHBvcnQge1xuICBjcmVhdGVTY2hlZHVsZXIsXG4gIHR5cGUgU2NoZWR1bGVyT3B0aW9ucyxcbiAgdHlwZSBTY2hlZHVsZXIsXG59IGZyb20gJy4vc2NoZWR1bGVyLmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZUNvbXBsaWFuY2VDbGllbnQsXG4gIHR5cGUgQ29tcGxpYW5jZUNoZWNrUmVzdWx0LFxuICB0eXBlIENvbXBsaWFuY2VWaW9sYXRpb24sXG59IGZyb20gJy4vY29tcGxpYW5jZS1jbGllbnQuanMnO1xuZXhwb3J0IHtcbiAgcGx1Z2luUnVuc093bkltYWdlLFxuICBwbHVnaW5Db21wbGlhbmNlVGFncyxcbiAgZGVyaXZlUGx1Z2luSW1hZ2VDb21wbGlhbmNlLFxuICB0eXBlIFBsdWdpbkNvbXBsaWFuY2VBdHRyaWJ1dGVzLFxuICBQTFVHSU5fSU1BR0VfQ09NUExJQU5DRV9GSUVMRFMsXG4gIHR5cGUgUGx1Z2luSW1hZ2VSb3csXG59IGZyb20gJy4uL3V0aWxzL3BsdWdpbi1jb21wbGlhbmNlLmpzJztcbmV4cG9ydCAqIGZyb20gJy4vY29tcGxpYW5jZS1ldmVudC1zdWJzY3JpYmVyLmpzJztcbmV4cG9ydCB7XG4gIHR5cGUgRW50aXR5RXZlbnQsXG4gIHR5cGUgRW50aXR5RXZlbnRTdWJzY3JpYmVyLFxuICBlbnRpdHlFdmVudHMsXG59IGZyb20gJy4vZW50aXR5LWV2ZW50cy5qcyc7XG5leHBvcnQge1xuICBkZXN0cm95U2hhcmVkSHR0cEFnZW50cyxcbiAgY3JlYXRlU2FmZUNsaWVudCxcbiAgUmVzcG9uc2VUb29MYXJnZUVycm9yLFxuICBJbnRlcm5hbEh0dHBDbGllbnQsXG59IGZyb20gJy4vaHR0cC1jbGllbnQuanMnO1xuZXhwb3J0IHtcbiAgc2V0UGxhdGZvcm1Kd2tzQ2FjaGUsXG4gIEp3a3NVbmF2YWlsYWJsZUVycm9yLFxuICBVbmtub3duS2lkRXJyb3IsXG4gIEp3a3NDYWNoZSxcbn0gZnJvbSAnLi9qd2tzLWNhY2hlLmpzJztcbi8vIFNlcnZpY2Uta2V5IGludGVybmFscyBzdGF5IGludGVybmFsLiBUaGUgcGFja2FnZSBzdXJmYWNlIGlzIHRoZSB2ZXJpZnkvaW5zcGVjdFxuLy8gc2lkZSB0aGF0IHBsYXRmb3JtJ3MgSldUIG9wdGlvbnMgYW5kIHRoZSBhdXRoIG1pZGRsZXdhcmUgbmVlZDsga2V5IE1BVEVSSUFMXG4vLyBoYW5kbGluZyAoYHNpZ25TZXJ2aWNlSnd0YCwgdGhlIGJ1bmRsZSwgdGhlIHRlc3QgcmVzZXQgaG9vaykgaXMgcmVhY2hhYmxlIG9ubHlcbi8vIGJ5IGRlZXAgaW1wb3J0IGZyb20gaW5zaWRlIGFwaS1jb3JlIGFuZCBpdHMgdGVzdGluZyBoZWxwZXJzLlxuZXhwb3J0IHtcbiAgaXNTZXJ2aWNlS2lkLFxuICBzZXJ2aWNlSWRlbnRpdHksXG4gIHZlcmlmeVNlcnZpY2VKd3QsXG59IGZyb20gJy4vc2VydmljZS1rZXlzLmpzJztcbi8vIFRoZSBicmVha2VyIGlzIHdpcmVkIGJ5IHRoZSBzaGFyZWQgSFRUUCBjbGllbnQ7IHNlcnZpY2VzIGNvbnN1bWUgaXRzIEVGRkVDVCxcbi8vIG5vdCB0aGUgY2xhc3MuIE9ubHkgdGhlIG9wZXJhdGlvbmFsIHN1cmZhY2UgaXMgcHVibGljLlxuZXhwb3J0IHtcbiAgcmVzZXRDaXJjdWl0QnJlYWtlcnMsXG59IGZyb20gJy4vY2lyY3VpdC1icmVha2VyLmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZUVudlJlZGlzRHVyYWJsZUV2ZW50QnVzLFxuICB0eXBlIEV2ZW50U3Vic2NyaXB0aW9uLFxuICB0eXBlIER1cmFibGVFdmVudEJ1cyxcbn0gZnJvbSAnLi9kdXJhYmxlLWV2ZW50LWJ1cy5qcyc7XG5leHBvcnQge1xuICBnZXRFcnJvclJldHJ5RGVjaXNpb24sXG59IGZyb20gJy4vcmV0cnktc3RyYXRlZ3kuanMnO1xuZXhwb3J0IHR5cGUge1xuICBSZXRyeUNvbmZpZyxcbn0gZnJvbSAnLi9yZXRyeS1zdHJhdGVneS5qcyc7XG5leHBvcnQge1xuICBuZXh0QmFja29mZk1zLFxuICByZXRyeUZvcmV2ZXIsXG59IGZyb20gJy4vcmV0cnktZm9yZXZlci5qcyc7XG5leHBvcnQge1xuICBnZXRRdW90YVNlcnZpY2VBdXRoSGVhZGVyLFxuICBjcmVhdGVRdW90YVNlcnZpY2UsXG4gIGluY3JlbWVudFF1b3RhLFxuICByZXNlcnZlUXVvdGEsXG4gIHNlbmRRdW90YVJlc2VydmVEZW5pZWQsXG4gIGRlY3JlbWVudFF1b3RhLFxuICB0eXBlIFF1b3RhUmVzZXJ2ZVJlc3VsdCxcbiAgdHlwZSBRdW90YVNlcnZpY2UsXG59IGZyb20gJy4vcXVvdGEuanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlUmVtb3RlQXVkaXRDbGllbnQsXG4gIHdpcmVBdXRoekRlbmlhbEF1ZGl0b3IsXG4gIHJlY29yZEF1ZGl0LFxuICB0eXBlIFJlbW90ZUF1ZGl0RXZlbnQsXG4gIHR5cGUgUmVtb3RlQXVkaXRDbGllbnQsXG59IGZyb20gJy4vcmVtb3RlLWF1ZGl0LWNsaWVudC5qcyc7XG5leHBvcnQge1xuICBhdWRpdFNwb29sS2V5LFxuICBjcmVhdGVFbnZSZWRpc0F1ZGl0U3Bvb2wsXG4gIHR5cGUgQXVkaXRTcG9vbCxcbn0gZnJvbSAnLi9hdWRpdC1zcG9vbC5qcyc7XG5leHBvcnQge1xuICByZXNvbHZlUmVkaXNDb25uZWN0aW9uLFxuICBkZXNjcmliZVJlZGlzQ29ubmVjdGlvbixcbiAgY3JlYXRlUmVkaXNDbGllbnQsXG4gIGNyZWF0ZUVudlJlZGlzQ2xpZW50LFxuICBjcmVhdGVSZWRpc1JlYWR5R2F0ZSxcbiAgaW5jcldpbmRvdyxcbiAgUmVkaXNDb25maWdFcnJvcixcbiAgdHlwZSBSZWRpc0V2YWxDbGllbnQsXG59IGZyb20gJy4vZW52LXJlZGlzLmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZVJlZGlzVG9rZW5SZXZvY2F0aW9uU3RvcmUsXG4gIGNyZWF0ZUVudlJlZGlzVG9rZW5SZXZvY2F0aW9uU3RvcmUsXG4gIHB1Ymxpc2hUb2tlblJldm9jYXRpb24sXG4gIHB1Ymxpc2hTZXNzaW9uUmV2b2NhdGlvbixcbiAgcHVibGlzaENyZWRlbnRpYWxSZXZvY2F0aW9uLFxufSBmcm9tICcuL3Rva2VuLXJldm9jYXRpb24uanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlTWVtb3J5U3NlVGlja2V0U3RvcmUsXG4gIGNyZWF0ZUVudlNzZVRpY2tldFN0b3JlLFxuICBlbnZTc2VUaWNrZXRDYXBzLFxuICB0eXBlIEVudlNzZVRpY2tldFN0b3JlQ29uZmlnLFxuICB0eXBlIFNzZVRpY2tldFN0b3JlLFxuICB0eXBlIFNzZVRpY2tldFN0b3JlQ29uZmlnLFxufSBmcm9tICcuL3NzZS10aWNrZXQtc3RvcmUuanMnO1xuZXhwb3J0IHtcbiAgd2lyZVNlcnZpY2VTZWN1cml0eSxcbn0gZnJvbSAnLi9zZXJ2aWNlLWJvb3QuanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlV2ViaG9va0NoYW5uZWwsXG4gIGNyZWF0ZUVtYWlsQ2hhbm5lbCxcbiAgY3JlYXRlQ2hhbm5lbFJlZ2lzdHJ5LFxuICB0eXBlIE5vdGlmaWNhdGlvblByaW9yaXR5LFxuICB0eXBlIE5vdGlmaWNhdGlvbk1lc3NhZ2UsXG4gIHR5cGUgQ2hhbm5lbFRhcmdldCxcbiAgdHlwZSBEZWxpdmVyeVJlc3VsdCxcbiAgdHlwZSBOb3RpZmljYXRpb25DaGFubmVsLFxufSBmcm9tICcuL25vdGlmaWNhdGlvbi1jaGFubmVscy5qcyc7XG5leHBvcnQge1xuICBjcmVhdGVFY29zeXN0ZW1Ob3RpZnlDbGllbnQsXG4gIEVDT1NZU1RFTV9OT1RJRllfUEFUSCxcbiAgdHlwZSBFY29zeXN0ZW1Ob3RpZnlDbGllbnQsXG59IGZyb20gJy4vZWNvc3lzdGVtLW5vdGlmeS1jbGllbnQuanMnO1xuIl19
41
+ export { fetchOrgAIAccess, fetchOrgAIKey, assertOrgAIEnabled, requireOrgAIEnabled, resetOrgAICache, ORG_AI_ACCESS_PATH, ORG_AI_KEYS_PATH, } from './org-ai-client.js';
42
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvc2VydmljZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLEVBQ0wsY0FBYyxFQUNkLHdCQUF3QixFQUN4QixtQkFBbUIsRUFDbkIsOEJBQThCLEdBQy9CLE1BQU0sdUJBQXVCLENBQUM7QUFDL0IsT0FBTyxFQUNMLGtCQUFrQixFQUtsQixZQUFZLEdBQ2IsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsY0FBYyxFQUNkLGtCQUFrQixFQUNsQixhQUFhLEVBQ2IsMEJBQTBCLEdBSzNCLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FBTyxFQUNMLHNCQUFzQixFQUN0QixrQkFBa0IsR0FFbkIsTUFBTSwwQkFBMEIsQ0FBQztBQUNsQyxPQUFPLEVBQ0wsZUFBZSxHQUdoQixNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsZ0JBQWdCLEVBQ2hCLGFBQWEsRUFDYixZQUFZLEVBQ1osYUFBYSxFQUNiLGNBQWMsRUFDZCxVQUFVLEVBQ1YsV0FBVyxFQUNYLGVBQWUsRUFDZixlQUFlLEVBQ2YscUNBQXFDLEdBU3RDLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUNMLHNCQUFzQixHQUd2QixNQUFNLHdCQUF3QixDQUFDO0FBQ2hDLE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsb0JBQW9CLEVBQ3BCLDJCQUEyQixFQUUzQiw4QkFBOEIsR0FFL0IsTUFBTSwrQkFBK0IsQ0FBQztBQUN2QyxjQUFjLGtDQUFrQyxDQUFDO0FBQ2pELE9BQU8sRUFHTCxZQUFZLEdBQ2IsTUFBTSxvQkFBb0IsQ0FBQztBQUM1QixPQUFPLEVBQ0wsdUJBQXVCLEVBQ3ZCLGdCQUFnQixFQUNoQixxQkFBcUIsRUFDckIsa0JBQWtCLEdBQ25CLE1BQU0sa0JBQWtCLENBQUM7QUFDMUIsT0FB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Q2xCLG1CQUFtQixFQUNuQixlQUFlLEVBQ2Ysa0JBQWtCLEVBQ2xCLGdCQUFnQixHQUVqQixNQUFNLG9CQUFvQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmV4cG9ydCB7XG4gIGV4Y2hhbmdlQXBpS2V5LFxuICByZXNldEFwaUtleUV4Y2hhbmdlQ2FjaGUsXG4gIEFwaUtleVJlamVjdGVkRXJyb3IsXG4gIEFwaUtleUV4Y2hhbmdlVW5hdmFpbGFibGVFcnJvcixcbn0gZnJvbSAnLi9hcGkta2V5LWV4Y2hhbmdlLmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZUNhY2hlU2VydmljZSxcbiAgdHlwZSBSZWRpc0NhY2hlQ2xpZW50LFxuICB0eXBlIENhY2hlSW52YWxpZGF0aW9uTWVzc2FnZSxcbiAgdHlwZSBDYWNoZUludmFsaWRhdGlvbkJ1cyxcbiAgdHlwZSBSZWRpc0ludmFsaWRhdGlvbkNsaWVudCxcbiAgQ2FjaGVTZXJ2aWNlLFxufSBmcm9tICcuL2NhY2hlLXNlcnZpY2UuanMnO1xuZXhwb3J0IHtcbiAgd2l0aExlYWRlckxvY2ssXG4gIGNyZWF0ZUVudlJlZGlzTG9jayxcbiAgbGVhZGVyTG9ja0tleSxcbiAgREVGQVVMVF9MRUFERVJfTE9DS19UVExfTVMsXG4gIHR5cGUgTG9ja1JlZGlzLFxuICB0eXBlIExlYWRlckxvY2tSdW4sXG4gIHR5cGUgTGVhZGVyTG9ja09wdGlvbnMsXG4gIHR5cGUgTGVhZGVyTG9ja0Fib3J0UmVhc29uLFxufSBmcm9tICcuL2xlYWRlci1sb2NrLmpzJztcbmV4cG9ydCB7XG4gIHNlbmRTeXN0ZW1Ob3RpZmljYXRpb24sXG4gIFNZU1RFTV9OT1RJRllfUEFUSCxcbiAgdHlwZSBTeXN0ZW1Ob3RpZmljYXRpb24sXG59IGZyb20gJy4vc3lzdGVtLW5vdGlmaWNhdGlvbi5qcyc7XG5leHBvcnQge1xuICBjcmVhdGVTY2hlZHVsZXIsXG4gIHR5cGUgU2NoZWR1bGVyT3B0aW9ucyxcbiAgdHlwZSBTY2hlZHVsZXIsXG59IGZyb20gJy4vc2NoZWR1bGVyLmpzJztcbmV4cG9ydCB7XG4gIGFzc2Vzc1ZlcnNpb25WdWxucyxcbiAgdmVyc2lvblNjYW5GYWN0cyxcbiAgc2NhbkZyZXNobmVzcyxcbiAgc2Nhbldhcm5pbmdzLFxuICBsYXN0U2Nhbm5lZEF0LFxuICBzY2FuU3RhbGVIb3VycyxcbiAgc2V2ZXJpdHlPZixcbiAgbWF4U2V2ZXJpdHksXG4gIHNldmVyaXR5QXRMZWFzdCxcbiAgVlVMTl9TRVZFUklUSUVTLFxuICBzZXRWdWxuUG9saWN5Q29tcGxpYW5jZUNsaWVudEZvclRlc3RzLFxuICB0eXBlIFZ1bG5TZXZlcml0eSxcbiAgdHlwZSBWdWxuV2FybmluZyxcbiAgdHlwZSBTY2FubmVkUmVjb3JkLFxuICB0eXBlIFZ1bG5Db25zdW1wdGlvblBvbGljeSxcbiAgdHlwZSBWdWxuQWNjZXB0YW5jZSxcbiAgdHlwZSBWZXJzaW9uU2NhbkZhY3RzLFxuICB0eXBlIFZlcnNpb25WdWxuSW5wdXQsXG4gIHR5cGUgVmVyc2lvblZ1bG5Bc3Nlc3NtZW50LFxufSBmcm9tICcuL3ZlcnNpb24tdnVsbi1wb2xpY3kuanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlQ29tcGxpYW5jZUNsaWVudCxcbiAgdHlwZSBDb21wbGlhbmNlQ2hlY2tSZXN1bHQsXG4gIHR5cGUgQ29tcGxpYW5jZVZpb2xhdGlvbixcbn0gZnJvbSAnLi9jb21wbGlhbmNlLWNsaWVudC5qcyc7XG5leHBvcnQge1xuICBwbHVnaW5SdW5zT3duSW1hZ2UsXG4gIHBsdWdpbkNvbXBsaWFuY2VUYWdzLFxuICBkZXJpdmVQbHVnaW5JbWFnZUNvbXBsaWFuY2UsXG4gIHR5cGUgUGx1Z2luQ29tcGxpYW5jZUF0dHJpYnV0ZXMsXG4gIFBMVUdJTl9JTUFHRV9DT01QTElBTkNFX0ZJRUxEUyxcbiAgdHlwZSBQbHVnaW5JbWFnZVJvdyxcbn0gZnJvbSAnLi4vdXRpbHMvcGx1Z2luLWNvbXBsaWFuY2UuanMnO1xuZXhwb3J0ICogZnJvbSAnLi9jb21wbGlhbmNlLWV2ZW50LXN1YnNjcmliZXIuanMnO1xuZXhwb3J0IHtcbiAgdHlwZSBFbnRpdHlFdmVudCxcbiAgdHlwZSBFbnRpdHlFdmVudFN1YnNjcmliZXIsXG4gIGVudGl0eUV2ZW50cyxcbn0gZnJvbSAnLi9lbnRpdHktZXZlbnRzLmpzJztcbmV4cG9ydCB7XG4gIGRlc3Ryb3lTaGFyZWRIdHRwQWdlbnRzLFxuICBjcmVhdGVTYWZlQ2xpZW50LFxuICBSZXNwb25zZVRvb0xhcmdlRXJyb3IsXG4gIEludGVybmFsSHR0cENsaWVudCxcbn0gZnJvbSAnLi9odHRwLWNsaWVudC5qcyc7XG5leHBvcnQge1xuICBzZXRQbGF0Zm9ybUp3a3NDYWNoZSxcbiAgSndrc1VuYXZhaWxhYmxlRXJyb3IsXG4gIFVua25vd25LaWRFcnJvcixcbiAgSndrc0NhY2hlLFxufSBmcm9tICcuL2p3a3MtY2FjaGUuanMnO1xuLy8gU2VydmljZS1rZXkgaW50ZXJuYWxzIHN0YXkgaW50ZXJuYWwuIFRoZSBwYWNrYWdlIHN1cmZhY2UgaXMgdGhlIHZlcmlmeS9pbnNwZWN0XG4vLyBzaWRlIHRoYXQgcGxhdGZvcm0ncyBKV1Qgb3B0aW9ucyBhbmQgdGhlIGF1dGggbWlkZGxld2FyZSBuZWVkOyBrZXkgTUFURVJJQUxcbi8vIGhhbmRsaW5nIChgc2lnblNlcnZpY2VKd3RgLCB0aGUgYnVuZGxlLCB0aGUgdGVzdCByZXNldCBob29rKSBpcyByZWFjaGFibGUgb25seVxuLy8gYnkgZGVlcCBpbXBvcnQgZnJvbSBpbnNpZGUgYXBpLWNvcmUgYW5kIGl0cyB0ZXN0aW5nIGhlbHBlcnMuXG5leHBvcnQge1xuICBpc1NlcnZpY2VLaWQsXG4gIHNlcnZpY2VJZGVudGl0eSxcbiAgdmVyaWZ5U2VydmljZUp3dCxcbn0gZnJvbSAnLi9zZXJ2aWNlLWtleXMuanMnO1xuLy8gVGhlIGJyZWFrZXIgaXMgd2lyZWQgYnkgdGhlIHNoYXJlZCBIVFRQIGNsaWVudDsgc2VydmljZXMgY29uc3VtZSBpdHMgRUZGRUNULFxuLy8gbm90IHRoZSBjbGFzcy4gT25seSB0aGUgb3BlcmF0aW9uYWwgc3VyZmFjZSBpcyBwdWJsaWMuXG5leHBvcnQge1xuICByZXNldENpcmN1aXRCcmVha2Vycyxcbn0gZnJvbSAnLi9jaXJjdWl0LWJyZWFrZXIuanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlRW52UmVkaXNEdXJhYmxlRXZlbnRCdXMsXG4gIHR5cGUgRXZlbnRTdWJzY3JpcHRpb24sXG4gIHR5cGUgRHVyYWJsZUV2ZW50QnVzLFxufSBmcm9tICcuL2R1cmFibGUtZXZlbnQtYnVzLmpzJztcbmV4cG9ydCB7XG4gIGdldEVycm9yUmV0cnlEZWNpc2lvbixcbn0gZnJvbSAnLi9yZXRyeS1zdHJhdGVneS5qcyc7XG5leHBvcnQgdHlwZSB7XG4gIFJldHJ5Q29uZmlnLFxufSBmcm9tICcuL3JldHJ5LXN0cmF0ZWd5LmpzJztcbmV4cG9ydCB7XG4gIG5leHRCYWNrb2ZmTXMsXG4gIHJldHJ5Rm9yZXZlcixcbn0gZnJvbSAnLi9yZXRyeS1mb3JldmVyLmpzJztcbmV4cG9ydCB7XG4gIGdldFF1b3RhU2VydmljZUF1dGhIZWFkZXIsXG4gIGNyZWF0ZVF1b3RhU2VydmljZSxcbiAgaW5jcmVtZW50UXVvdGEsXG4gIHJlc2VydmVRdW90YSxcbiAgc2VuZFF1b3RhUmVzZXJ2ZURlbmllZCxcbiAgZGVjcmVtZW50UXVvdGEsXG4gIG1ldGVyZWRRdW90YVR5cGVGb3IsXG4gIGNhbGxRdW90YUV4ZW1wdGlvbixcbiAgUVVPVEFfQ0hFQ0tfVElNRU9VVF9NUyxcbiAgcXVvdGFBdHRyaWJ1dGlvbkZyb21SZXF1ZXN0LFxuICB0eXBlIEFpS2V5U291cmNlLFxuICB0eXBlIEFpTGltaXRDb2RlLFxuICB0eXBlIENhbGxRdW90YUV4ZW1wdGlvbixcbiAgdHlwZSBRdW90YVJlc2VydmVSZXN1bHQsXG4gIHR5cGUgUXVvdGFUb2tlblVzYWdlLFxuICB0eXBlIFF1b3RhU2VydmljZSxcbiAgdHlwZSBRdW90YVVzYWdlQXR0cmlidXRpb24sXG59IGZyb20gJy4vcXVvdGEuanMnO1xuZXhwb3J0IHtcbiAgYWlDYWxsV2VpZ2h0LFxuICBwYXJzZUFpTW9kZWxXZWlnaHRzLFxuICBNQVhfQUlfQ0FMTF9XRUlHSFQsXG59IGZyb20gJy4vYWktY2FsbC13ZWlnaHQuanMnO1xuZXhwb3J0IHtcbiAgUXVvdGFJbmNyZW1lbnRCYXRjaGVyLFxuICBmbHVzaFF1b3RhSW5jcmVtZW50cyxcbiAgUVVPVEFfSU5DUkVNRU5UX0ZMVVNIX01TLFxuICBRVU9UQV9JTkNSRU1FTlRfQlVGRkVSX01BWCxcbiAgTUFYX0dST1VQU19QRVJfSU5DUkVNRU5ULFxuICB0eXBlIE1ldGVyZWRJbmNyZW1lbnRHcm91cCxcbn0gZnJvbSAnLi9xdW90YS1pbmNyZW1lbnQtYmF0Y2hlci5qcyc7XG5leHBvcnQge1xuICBjcmVhdGVSZW1vdGVBdWRpdENsaWVudCxcbiAgd2lyZUF1dGh6RGVuaWFsQXVkaXRvcixcbiAgcmVjb3JkQXVkaXQsXG4gIHR5cGUgUmVtb3RlQXVkaXRFdmVudCxcbiAgdHlwZSBSZW1vdGVBdWRpdFdpcmVFdmVudCxcbiAgdHlwZSBSZW1vdGVBdWRpdENsaWVudCxcbn0gZnJvbSAnLi9yZW1vdGUtYXVkaXQtY2xpZW50LmpzJztcbmV4cG9ydCB7XG4gIHJ1bldpdGhBdWRpdENvbnRleHQsXG4gIGN1cnJlbnRBdWRpdENvbnRleHQsXG4gIGF1ZGl0Q29udGV4dEZyb21SZXF1ZXN0LFxuICBhdWRpdEF1dGhCbG9jayxcbiAgc2FuaXRpemVDb3JyZWxhdGlvbklkLFxuICBzYW5pdGl6ZVVzZXJBZ2VudCxcbiAgQVVESVRfQ09SUkVMQVRJT05fSEVBREVSUyxcbiAgdHlwZSBBdWRpdENvbnRleHQsXG4gIHR5cGUgQXVkaXRDb3JyZWxhdGlvbixcbiAgdHlwZSBBdWRpdEpvYkNvbnRleHQsXG4gIHR5cGUgQXVkaXRBdXRoQmxvY2ssXG59IGZyb20gJy4vYXVkaXQtY29udGV4dC5qcyc7XG5leHBvcnQge1xuICBhdWRpdFNwb29sS2V5LFxuICBjcmVhdGVFbnZSZWRpc0F1ZGl0U3Bvb2wsXG4gIHR5cGUgQXVkaXRTcG9vbCxcbn0gZnJvbSAnLi9hdWRpdC1zcG9vbC5qcyc7XG5leHBvcnQge1xuICByZXNvbHZlUmVkaXNDb25uZWN0aW9uLFxuICBkZXNjcmliZVJlZGlzQ29ubmVjdGlvbixcbiAgY3JlYXRlUmVkaXNDbGllbnQsXG4gIGNyZWF0ZUVudlJlZGlzQ2xpZW50LFxuICBjcmVhdGVSZWRpc1JlYWR5R2F0ZSxcbiAgaW5jcldpbmRvdyxcbiAgUmVkaXNDb25maWdFcnJvcixcbiAgdHlwZSBSZWRpc0V2YWxDbGllbnQsXG59IGZyb20gJy4vZW52LXJlZGlzLmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZVJlZGlzVG9rZW5SZXZvY2F0aW9uU3RvcmUsXG4gIGNyZWF0ZUVudlJlZGlzVG9rZW5SZXZvY2F0aW9uU3RvcmUsXG4gIHB1Ymxpc2hUb2tlblJldm9jYXRpb24sXG4gIHB1Ymxpc2hTZXNzaW9uUmV2b2NhdGlvbixcbiAgcHVibGlzaENyZWRlbnRpYWxSZXZvY2F0aW9uLFxufSBmcm9tICcuL3Rva2VuLXJldm9jYXRpb24uanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlTWVtb3J5U3NlVGlja2V0U3RvcmUsXG4gIGNyZWF0ZUVudlNzZVRpY2tldFN0b3JlLFxuICBlbnZTc2VUaWNrZXRDYXBzLFxuICB0eXBlIEVudlNzZVRpY2tldFN0b3JlQ29uZmlnLFxuICB0eXBlIFNzZVRpY2tldFN0b3JlLFxuICB0eXBlIFNzZVRpY2tldFN0b3JlQ29uZmlnLFxufSBmcm9tICcuL3NzZS10aWNrZXQtc3RvcmUuanMnO1xuZXhwb3J0IHtcbiAgd2lyZVNlcnZpY2VTZWN1cml0eSxcbn0gZnJvbSAnLi9zZXJ2aWNlLWJvb3QuanMnO1xuZXhwb3J0IHtcbiAgY3JlYXRlV2ViaG9va0NoYW5uZWwsXG4gIGNyZWF0ZUVtYWlsQ2hhbm5lbCxcbiAgY3JlYXRlQ2hhbm5lbFJlZ2lzdHJ5LFxuICB0eXBlIE5vdGlmaWNhdGlvblByaW9yaXR5LFxuICB0eXBlIE5vdGlmaWNhdGlvbk1lc3NhZ2UsXG4gIHR5cGUgQ2hhbm5lbFRhcmdldCxcbiAgdHlwZSBEZWxpdmVyeVJlc3VsdCxcbiAgdHlwZSBOb3RpZmljYXRpb25DaGFubmVsLFxufSBmcm9tICcuL25vdGlmaWNhdGlvbi1jaGFubmVscy5qcyc7XG5leHBvcnQge1xuICBhbGVydERlbGl2ZXJ5Q29uZmlnLFxuICBjYW5EZWxpdmVyQW55d2hlcmUsXG4gIGRlYWRtYW5Db25maWcsXG4gIHJlcG9ydEFsZXJ0RGVsaXZlcnlQb3N0dXJlLFxuICBzbGFja0FsZXJ0c0NvbmZpZyxcbiAgc3RhcnREZWFkbWFuSGVhcnRiZWF0LFxuICBzdG9wRGVhZG1hbkhlYXJ0YmVhdCxcbiAgdHlwZSBEZWFkbWFuQ29uZmlnLFxuICB0eXBlIEFsZXJ0RGVsaXZlcnlDb25maWcsXG4gIHR5cGUgU2xhY2tBbGVydHNDb25maWcsXG59IGZyb20gJy4vYWxlcnQtZGVsaXZlcnkuanMnO1xuZXhwb3J0IHtcbiAgdmVyaWZ5UGFnZXJEdXR5U2lnbmF0dXJlLFxuICBvbkNhbGxJbmJvdW5kQ29uZmlnLFxuICBmZXRjaFJvdGFDb3ZlcmFnZSxcbiAgcGFyc2VPbkNhbGxXZWJob29rLFxuICBhY2tMYXRlbmN5U2Vjb25kcyxcbiAgUEFHRVJEVVRZX0FQSV9VUkwsXG4gIHR5cGUgT25DYWxsSW5ib3VuZENvbmZpZyxcbiAgdHlwZSBPbkNhbGxXZWJob29rRXZlbnQsXG4gIHR5cGUgT25DYWxsV2ViaG9va0V2ZW50VHlwZSxcbiAgdHlwZSBSb3RhQ292ZXJhZ2UsXG59IGZyb20gJy4vcGFnZXJkdXR5LWluYm91bmQuanMnO1xuZXhwb3J0IHtcbiAgc2VuZFBhZ2VyRHV0eUV2ZW50LFxuICBwYWdlckR1dHlDb25maWcsXG4gIHJlc29sdmVFdmVudHNFbmRwb2ludCxcbiAgUEFHRVJEVVRZX0VWRU5UU19VUkwsXG4gIHR5cGUgT25DYWxsUHJvdmlkZXIsXG4gIHR5cGUgUGFnZXJEdXR5QWN0aW9uLFxuICB0eXBlIFBhZ2VyRHV0eUNvbmZpZyxcbiAgdHlwZSBQYWdlckR1dHlFdmVudCxcbiAgdHlwZSBQYWdlckR1dHlSZXN1bHQsXG4gIHR5cGUgUGFnZXJEdXR5U2V2ZXJpdHksXG59IGZyb20gJy4vcGFnZXJkdXR5LmpzJztcbmV4cG9ydCB7XG4gIGNyZWF0ZUVjb3N5c3RlbU5vdGlmeUNsaWVudCxcbiAgRUNPU1lTVEVNX05PVElGWV9QQVRILFxuICB0eXBlIEVjb3N5c3RlbU5vdGlmeUNsaWVudCxcbn0gZnJvbSAnLi9lY29zeXN0ZW0tbm90aWZ5LWNsaWVudC5qcyc7XG5leHBvcnQge1xuICBmZXRjaE9yZ0FJQWNjZXNzLFxuICBmZXRjaE9yZ0FJS2V5LFxuICBhc3NlcnRPcmdBSUVuYWJsZWQsXG4gIHJlcXVpcmVPcmdBSUVuYWJsZWQsXG4gIHJlc2V0T3JnQUlDYWNoZSxcbiAgT1JHX0FJX0FDQ0VTU19QQVRILFxuICBPUkdfQUlfS0VZU19QQVRILFxuICB0eXBlIE9yZ0FJQWNjZXNzLFxufSBmcm9tICcuL29yZy1haS1jbGllbnQuanMnO1xuIl19
@@ -0,0 +1,61 @@
1
+ /**
2
+ * The org half of every AI call: is AI switched on for this organization, which
3
+ * providers has it configured its OWN key for, and — when the call is going to
4
+ * use one — the decrypted key.
5
+ *
6
+ * Platform holds both facts (the org's `aiEnabled` switch and its encrypted
7
+ * `aiProviderKeys`), so they are two INTERNAL reads on platform, gated by
8
+ * `requireInternalService` to the four AI callers (ask, pipeline, plugin,
9
+ * reporting) and by the mesh on the Istio targets:
10
+ *
11
+ * - `GET /internal/ai/access/:orgId` → `{ aiEnabled, providers: string[] }`
12
+ * - `GET /internal/ai/keys/:orgId/:provider` → `{ provider, apiKey }` (audited on platform)
13
+ *
14
+ * KEY MATERIAL HANDLING. A key fetched here is held in process memory for at most
15
+ * {@link ORG_AI_KEY_TTL_MS} (so a rotated or cleared key stops being used within
16
+ * that window, and platform's read audit is not one row per model call), is never
17
+ * logged, never written to Redis or any shared cache, and never placed in an error
18
+ * message. Callers hand it straight to a model factory.
19
+ *
20
+ * FAILURE IS NOT A FALLBACK. An unreachable platform makes {@link fetchOrgAIAccess}
21
+ * throw an {@link AIConfigurationError} rather than answer "enabled, no org keys":
22
+ * that answer would silently bill the platform's key for an org that configured its
23
+ * own, and would run AI for an org whose administrators switched it off.
24
+ */
25
+ import type { NextFunction, Request, Response } from 'express';
26
+ /** Platform paths (exported for the platform route-coverage + mesh declarations). */
27
+ export declare const ORG_AI_ACCESS_PATH = "/internal/ai/access";
28
+ export declare const ORG_AI_KEYS_PATH = "/internal/ai/keys";
29
+ /** How long an org's switch + configured-provider list is reused (default 30s). */
30
+ export declare const ORG_AI_ACCESS_TTL_MS: number;
31
+ /** How long a decrypted org key is held in memory (default 60s; 0 disables caching). */
32
+ export declare const ORG_AI_KEY_TTL_MS: number;
33
+ /** An org's AI posture, as platform reports it. */
34
+ export interface OrgAIAccess {
35
+ /** False when the org's administrators switched AI features off. */
36
+ aiEnabled: boolean;
37
+ /** Provider ids the org has configured its OWN key for (catalog ids). */
38
+ providers: string[];
39
+ }
40
+ /**
41
+ * The org's AI switch and the providers it has its own key for. Cached
42
+ * {@link ORG_AI_ACCESS_TTL_MS}. Throws {@link AIConfigurationError} (503) when platform
43
+ * cannot answer — see the module comment for why that is not "enabled".
44
+ */
45
+ export declare function fetchOrgAIAccess(orgId: string): Promise<OrgAIAccess>;
46
+ /**
47
+ * The org's decrypted key for `provider`. Call ONLY after {@link fetchOrgAIAccess}
48
+ * listed the provider. Throws {@link AIConfigurationError} when the key cannot be
49
+ * loaded — the caller must then fail the request, never fall back to the platform key.
50
+ */
51
+ export declare function fetchOrgAIKey(orgId: string, provider: string): Promise<string>;
52
+ /** Throw {@link AIDisabledError} (403 `AI_DISABLED`) when the org switched AI off. */
53
+ export declare function assertOrgAIEnabled(orgId: string): Promise<OrgAIAccess>;
54
+ /**
55
+ * Route guard: refuse every AI entry point for an org whose administrators switched
56
+ * AI off (403 `AI_DISABLED`), or whose settings platform cannot confirm (503). Mount
57
+ * AFTER auth and BEFORE any quota reservation, so a disabled org is never charged.
58
+ */
59
+ export declare function requireOrgAIEnabled(req: Request, res: Response, next: NextFunction): Promise<void>;
60
+ /** Drop the cached org AI posture + keys (tests; an org whose settings just changed). */
61
+ export declare function resetOrgAICache(orgId?: string): void;
@@ -0,0 +1,112 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { createSafeClient } from './http-client.js';
4
+ import { serviceIdentity } from './service-keys.js';
5
+ import { AIConfigurationError, AIDisabledError } from '../errors/app-errors.js';
6
+ import { getServiceAuthHeader } from '../middleware/service-tokens.js';
7
+ import { ErrorCode } from '../types/error-codes.js';
8
+ import { envInt } from '../utils/env.js';
9
+ import { sendError } from '../utils/response.js';
10
+ import { serviceEndpoint } from '../utils/service-registry.js';
11
+ /** Platform paths (exported for the platform route-coverage + mesh declarations). */
12
+ export const ORG_AI_ACCESS_PATH = '/internal/ai/access';
13
+ export const ORG_AI_KEYS_PATH = '/internal/ai/keys';
14
+ /** How long an org's switch + configured-provider list is reused (default 30s). */
15
+ export const ORG_AI_ACCESS_TTL_MS = envInt('ORG_AI_ACCESS_TTL_MS', 30_000, { min: 0 });
16
+ /** How long a decrypted org key is held in memory (default 60s; 0 disables caching). */
17
+ export const ORG_AI_KEY_TTL_MS = envInt('ORG_AI_KEY_TTL_MS', 60_000, { min: 0, max: 300_000 });
18
+ const accessCache = new Map();
19
+ const keyCache = new Map();
20
+ let client;
21
+ function platform() {
22
+ client ??= createSafeClient({ ...serviceEndpoint('platform'), timeout: 5_000 });
23
+ return client;
24
+ }
25
+ /** A service token scoped to the org being asked about — platform binds the read to it. */
26
+ function authFor(orgId) {
27
+ return { Authorization: getServiceAuthHeader({ serviceName: serviceIdentity(), orgId, role: 'member' }) };
28
+ }
29
+ /**
30
+ * The org's AI switch and the providers it has its own key for. Cached
31
+ * {@link ORG_AI_ACCESS_TTL_MS}. Throws {@link AIConfigurationError} (503) when platform
32
+ * cannot answer — see the module comment for why that is not "enabled".
33
+ */
34
+ export async function fetchOrgAIAccess(orgId) {
35
+ const hit = accessCache.get(orgId);
36
+ if (hit && hit.expiresAt > Date.now())
37
+ return hit.value;
38
+ const response = await platform().get(`${ORG_AI_ACCESS_PATH}/${encodeURIComponent(orgId)}`, { headers: authFor(orgId) });
39
+ const body = response?.body;
40
+ const data = body?.data ?? body;
41
+ if (!response || response.statusCode !== 200 || typeof data?.aiEnabled !== 'boolean') {
42
+ throw new AIConfigurationError('The organization\'s AI settings could not be read, so no AI request was made. Try again shortly.');
43
+ }
44
+ const value = {
45
+ aiEnabled: data.aiEnabled,
46
+ providers: Array.isArray(data.providers) ? data.providers.filter((p) => typeof p === 'string') : [],
47
+ };
48
+ if (ORG_AI_ACCESS_TTL_MS > 0)
49
+ accessCache.set(orgId, { value, expiresAt: Date.now() + ORG_AI_ACCESS_TTL_MS });
50
+ return value;
51
+ }
52
+ /**
53
+ * The org's decrypted key for `provider`. Call ONLY after {@link fetchOrgAIAccess}
54
+ * listed the provider. Throws {@link AIConfigurationError} when the key cannot be
55
+ * loaded — the caller must then fail the request, never fall back to the platform key.
56
+ */
57
+ export async function fetchOrgAIKey(orgId, provider) {
58
+ const cacheKey = `${orgId}\u0000${provider}`;
59
+ const hit = keyCache.get(cacheKey);
60
+ if (hit && hit.expiresAt > Date.now())
61
+ return hit.value;
62
+ const response = await platform().get(`${ORG_AI_KEYS_PATH}/${encodeURIComponent(orgId)}/${encodeURIComponent(provider)}`, { headers: authFor(orgId) });
63
+ const apiKey = response?.body?.data?.apiKey ?? response?.body?.apiKey;
64
+ if (!response || response.statusCode !== 200 || typeof apiKey !== 'string' || apiKey.length === 0) {
65
+ // Status only: the body is never echoed, and the key is never in a message.
66
+ throw new AIConfigurationError(`The organization's ${provider} API key could not be loaded, so the request was not sent. `
67
+ + 'It was NOT retried on the platform\'s key — re-save the key in Settings, or try again shortly.');
68
+ }
69
+ if (ORG_AI_KEY_TTL_MS > 0)
70
+ keyCache.set(cacheKey, { value: apiKey, expiresAt: Date.now() + ORG_AI_KEY_TTL_MS });
71
+ return apiKey;
72
+ }
73
+ /** Throw {@link AIDisabledError} (403 `AI_DISABLED`) when the org switched AI off. */
74
+ export async function assertOrgAIEnabled(orgId) {
75
+ const access = await fetchOrgAIAccess(orgId);
76
+ if (!access.aiEnabled)
77
+ throw new AIDisabledError();
78
+ return access;
79
+ }
80
+ /**
81
+ * Route guard: refuse every AI entry point for an org whose administrators switched
82
+ * AI off (403 `AI_DISABLED`), or whose settings platform cannot confirm (503). Mount
83
+ * AFTER auth and BEFORE any quota reservation, so a disabled org is never charged.
84
+ */
85
+ export async function requireOrgAIEnabled(req, res, next) {
86
+ const orgId = req.user?.organizationId;
87
+ if (!orgId) {
88
+ sendError(res, 403, 'No organization associated with this request', ErrorCode.INSUFFICIENT_PERMISSIONS);
89
+ return;
90
+ }
91
+ try {
92
+ await assertOrgAIEnabled(String(orgId));
93
+ next();
94
+ }
95
+ catch (err) {
96
+ const e = err;
97
+ sendError(res, e.statusCode ?? 503, e.message, e.code);
98
+ }
99
+ }
100
+ /** Drop the cached org AI posture + keys (tests; an org whose settings just changed). */
101
+ export function resetOrgAICache(orgId) {
102
+ if (orgId === undefined) {
103
+ accessCache.clear();
104
+ keyCache.clear();
105
+ return;
106
+ }
107
+ accessCache.delete(orgId);
108
+ for (const k of keyCache.keys())
109
+ if (k.startsWith(`${orgId}\u0000`))
110
+ keyCache.delete(k);
111
+ }
112
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JnLWFpLWNsaWVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zZXJ2aWNlcy9vcmctYWktY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUE0QnRDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQ3BELE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsZUFBZSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDaEYsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDdkUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3BELE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDakQsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRS9ELHFGQUFxRjtBQUNyRixNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxxQkFBcUIsQ0FBQztBQUN4RCxNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxtQkFBbUIsQ0FBQztBQUVwRCxtRkFBbUY7QUFDbkYsTUFBTSxDQUFDLE1BQU0sb0JBQW9CLEdBQUcsTUFBTSxDQUFDLHNCQUFzQixFQUFFLE1BQU0sRUFBRSxFQUFFLEdBQUcsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3ZGLHdGQUF3RjtBQUN4RixNQUFNLENBQUMsTUFBTSxpQkFBaUIsR0FBRyxNQUFNLENBQUMsbUJBQW1CLEVBQUUsTUFBTSxFQUFFLEVBQUUsR0FBRyxFQUFFLENBQUMsRUFBRSxHQUFHLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQztBQVkvRixNQUFNLFdBQVcsR0FBRyxJQUFJLEdBQUcsRUFBK0Is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LHdCQUF3QixDQUFDLENBQUM7UUFDeEcsT0FBTztJQUNULENBQUM7SUFDRCxJQUFJLENBQUM7UUFDSCxNQUFNLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQ3hDLElBQUksRUFBRSxDQUFDO0lBQ1QsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixNQUFNLENBQUMsR0FBRyxHQUE2QyxDQUFDO1FBQ3hELFNBQVMsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxDQUFDLFVBQVUsSUFBSSxHQUFHLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDekQsQ0FBQztBQUNILENBQUM7QUFFRCx5RkFBeUY7QUFDekYsTUFBTSxVQUFVLGVBQWUsQ0FBQyxLQUFjO0lBQzVDLElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1FBQ3hCLFdBQVcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUNwQixRQUFRLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDakIsT0FBTztJQUNULENBQUM7SUFDRCxXQUFXLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQzFCLEtBQUssTUFBTSxDQUFDLElBQUksUUFBUSxDQUFDLElBQUksRUFBRTtRQUFFLElBQUksQ0FBQyxDQUFDLFVBQVUsQ0FBQyxHQUFHLEtBQUssUUFBUSxDQUFDO1lBQUUsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUMxRixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogVGhlIG9yZyBoYWxmIG9mIGV2ZXJ5IEFJIGNhbGw6IGlzIEFJIHN3aXRjaGVkIG9uIGZvciB0aGlzIG9yZ2FuaXphdGlvbiwgd2hpY2hcbiAqIHByb3ZpZGVycyBoYXMgaXQgY29uZmlndXJlZCBpdHMgT1dOIGtleSBmb3IsIGFuZCDigJQgd2hlbiB0aGUgY2FsbCBpcyBnb2luZyB0b1xuICogdXNlIG9uZSDigJQgdGhlIGRlY3J5cHRlZCBrZXkuXG4gKlxuICogUGxhdGZvcm0gaG9sZHMgYm90aCBmYWN0cyAodGhlIG9yZydzIGBhaUVuYWJsZWRgIHN3aXRjaCBhbmQgaXRzIGVuY3J5cHRlZFxuICogYGFpUHJvdmlkZXJLZXlzYCksIHNvIHRoZXkgYXJlIHR3byBJTlRFUk5BTCByZWFkcyBvbiBwbGF0Zm9ybSwgZ2F0ZWQgYnlcbiAqIGByZXF1aXJlSW50ZXJuYWxTZXJ2aWNlYCB0byB0aGUgZm91ciBBSSBjYWxsZXJzIChhc2ssIHBpcGVsaW5lLCBwbHVnaW4sXG4gKiByZXBvcnRpbmcpIGFuZCBieSB0aGUgbWVzaCBvbiB0aGUgSXN0aW8gdGFyZ2V0czpcbiAqXG4gKiAgLSBgR0VUIC9pbnRlcm5hbC9haS9hY2Nlc3MvOm9yZ0lkYCAgICAgICAgIOKGkiBgeyBhaUVuYWJsZWQsIHByb3ZpZGVyczogc3RyaW5nW10gfWBcbiAqICAtIGBHRVQgL2ludGVybmFsL2FpL2tleXMvOm9yZ0lkLzpwcm92aWRlcmAg4oaSIGB7IHByb3ZpZGVyLCBhcGlLZXkgfWAgKGF1ZGl0ZWQgb24gcGxhdGZvcm0pXG4gKlxuICogS0VZIE1BVEVSSUFMIEhBTkRMSU5HLiBBIGtleSBmZXRjaGVkIGhlcmUgaXMgaGVsZCBpbiBwcm9jZXNzIG1lbW9yeSBmb3IgYXQgbW9zdFxuICoge0BsaW5rIE9SR19BSV9LRVlfVFRMX01TfSAoc28gYSByb3RhdGVkIG9yIGNsZWFyZWQga2V5IHN0b3BzIGJlaW5nIHVzZWQgd2l0aGluXG4gKiB0aGF0IHdpbmRvdywgYW5kIHBsYXRmb3JtJ3MgcmVhZCBhdWRpdCBpcyBub3Qgb25lIHJvdyBwZXIgbW9kZWwgY2FsbCksIGlzIG5ldmVyXG4gKiBsb2dnZWQsIG5ldmVyIHdyaXR0ZW4gdG8gUmVkaXMgb3IgYW55IHNoYXJlZCBjYWNoZSwgYW5kIG5ldmVyIHBsYWNlZCBpbiBhbiBlcnJvclxuICogbWVzc2FnZS4gQ2FsbGVycyBoYW5kIGl0IHN0cmFpZ2h0IHRvIGEgbW9kZWwgZmFjdG9yeS5cbiAqXG4gKiBGQUlMVVJFIElTIE5PVCBBIEZBTExCQUNLLiBBbiB1bnJlYWNoYWJsZSBwbGF0Zm9ybSBtYWtlcyB7QGxpbmsgZmV0Y2hPcmdBSUFjY2Vzc31cbiAqIHRocm93IGFuIHtAbGluayBBSUNvbmZpZ3VyYXRpb25FcnJvcn0gcmF0aGVyIHRoYW4gYW5zd2VyIFwiZW5hYmxlZCwgbm8gb3JnIGtleXNcIjpcbiAqIHRoYXQgYW5zd2VyIHdvdWxkIHNpbGVudGx5IGJpbGwgdGhlIHBsYXRmb3JtJ3Mga2V5IGZvciBhbiBvcmcgdGhhdCBjb25maWd1cmVkIGl0c1xuICogb3duLCBhbmQgd291bGQgcnVuIEFJIGZvciBhbiBvcmcgd2hvc2UgYWRtaW5pc3RyYXRvcnMgc3dpdGNoZWQgaXQgb2ZmLlxuICovXG5cbmltcG9ydCB0eXBlIHsgTmV4dEZ1bmN0aW9uLCBSZXF1ZXN0LCBSZXNwb25zZSB9IGZyb20gJ2V4cHJlc3MnO1xuaW1wb3J0IHsgY3JlYXRlU2FmZUNsaWVudCB9IGZyb20gJy4vaHR0cC1jbGllbnQuanMnO1xuaW1wb3J0IHsgc2VydmljZUlkZW50aXR5IH0gZnJvbSAnLi9zZXJ2aWNlLWtleXMuanMnO1xuaW1wb3J0IHsgQUlDb25maWd1cmF0aW9uRXJyb3IsIEFJRGlzYWJsZWRFcnJvciB9IGZyb20gJy4uL2Vycm9ycy9hcHAtZXJyb3JzLmpzJztcbmltcG9ydCB7IGdldFNlcnZpY2VBdXRoSGVhZGVyIH0gZnJvbSAnLi4vbWlkZGxld2FyZS9zZXJ2aWNlLXRva2Vucy5qcyc7XG5pbXBvcnQgeyBFcnJvckNvZGUgfSBmcm9tICcuLi90eXBlcy9lcnJvci1jb2Rlcy5qcyc7XG5pbXBvcnQgeyBlbnZJbnQgfSBmcm9tICcuLi91dGlscy9lbnYuanMnO1xuaW1wb3J0IHsgc2VuZEVycm9yIH0gZnJvbSAnLi4vdXRpbHMvcmVzcG9uc2UuanMnO1xuaW1wb3J0IHsgc2VydmljZUVuZHBvaW50IH0gZnJvbSAnLi4vdXRpbHMvc2VydmljZS1yZWdpc3RyeS5qcyc7XG5cbi8qKiBQbGF0Zm9ybSBwYXRocyAoZXhwb3J0ZWQgZm9yIHRoZSBwbGF0Zm9ybSByb3V0ZS1jb3ZlcmFnZSArIG1lc2ggZGVjbGFyYXRpb25zKS4gKi9cbmV4cG9ydCBjb25zdCBPUkdfQUlfQUNDRVNTX1BBVEggPSAnL2ludGVybmFsL2FpL2FjY2Vzcyc7XG5leHBvcnQgY29uc3QgT1JHX0FJX0tFWVNfUEFUSCA9ICcvaW50ZXJuYWwvYWkva2V5cyc7XG5cbi8qKiBIb3cgbG9uZyBhbiBvcmcncyBzd2l0Y2ggKyBjb25maWd1cmVkLXByb3ZpZGVyIGxpc3QgaXMgcmV1c2VkIChkZWZhdWx0IDMwcykuICovXG5leHBvcnQgY29uc3QgT1JHX0FJX0FDQ0VTU19UVExfTVMgPSBlbnZJbnQoJ09SR19BSV9BQ0NFU1NfVFRMX01TJywgMzBfMDAwLCB7IG1pbjogMCB9KTtcbi8qKiBIb3cgbG9uZyBhIGRlY3J5cHRlZCBvcmcga2V5IGlzIGhlbGQgaW4gbWVtb3J5IChkZWZhdWx0IDYwczsgMCBkaXNhYmxlcyBjYWNoaW5nKS4gKi9cbmV4cG9ydCBjb25zdCBPUkdfQUlfS0VZX1RUTF9NUyA9IGVudkludCgnT1JHX0FJX0tFWV9UVExfTVMnLCA2MF8wMDAsIHsgbWluOiAwLCBtYXg6IDMwMF8wMDAgfSk7XG5cbi8qKiBBbiBvcmcncyBBSSBwb3N0dXJlLCBhcyBwbGF0Zm9ybSByZXBvcnRzIGl0LiAqL1xuZXhwb3J0IGludGVyZmFjZSBPcmdBSUFjY2VzcyB7XG4gIC8qKiBGYWxzZSB3aGVuIHRoZSBvcmcncyBhZG1pbmlzdHJhdG9ycyBzd2l0Y2hlZCBBSSBmZWF0dXJlcyBvZmYuICovXG4gIGFpRW5hYmxlZDogYm9vbGVhbjtcbiAgLyoqIFByb3ZpZGVyIGlkcyB0aGUgb3JnIGhhcyBjb25maWd1cmVkIGl0cyBPV04ga2V5IGZvciAoY2F0YWxvZyBpZHMpLiAqL1xuICBwcm92aWRlcnM6IHN0cmluZ1tdO1xufVxuXG5pbnRlcmZhY2UgQ2FjaGVkPFQ+IHsgdmFsdWU6IFQ7IGV4cGlyZXNBdDogbnVtYmVyIH1cblxuY29uc3QgYWNjZXNzQ2FjaGUgPSBuZXcgTWFwPHN0cmluZywgQ2FjaGVkPE9yZ0FJQWNjZXNzPj4oKTtcbmNvbnN0IGtleUNhY2hlID0gbmV3IE1hcDxzdHJpbmcsIENhY2hlZDxzdHJpbmc+PigpO1xuXG5sZXQgY2xpZW50OiBSZXR1cm5UeXBlPHR5cGVvZiBjcmVhdGVTYWZlQ2xpZW50PiB8IHVuZGVmaW5lZDtcbmZ1bmN0aW9uIHBsYXRmb3JtKCk6IFJldHVyblR5cGU8dHlwZW9mIGNyZWF0ZVNhZmVDbGllbnQ+IHtcbiAgY2xpZW50ID8/PSBjcmVhdGVTYWZlQ2xpZW50KHsgLi4uc2VydmljZUVuZHBvaW50KCdwbGF0Zm9ybScpLCB0aW1lb3V0OiA1XzAwMCB9KTtcbiAgcmV0dXJuIGNsaWVudDtcbn1cblxuLyoqIEEgc2VydmljZSB0b2tlbiBzY29wZWQgdG8gdGhlIG9yZyBiZWluZyBhc2tlZCBhYm91dCDigJQgcGxhdGZvcm0gYmluZHMgdGhlIHJlYWQgdG8gaXQuICovXG5mdW5jdGlvbiBhdXRoRm9yKG9yZ0lkOiBzdHJpbmcpOiBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+IHtcbiAgcmV0dXJuIHsgQXV0aG9yaXphdGlvbjogZ2V0U2VydmljZUF1dGhIZWFkZXIoeyBzZXJ2aWNlTmFtZTogc2VydmljZUlkZW50aXR5KCksIG9yZ0lkLCByb2xlOiAnbWVtYmVyJyB9KSB9O1xufVxuXG4vKipcbiAqIFRoZSBvcmcncyBBSSBzd2l0Y2ggYW5kIHRoZSBwcm92aWRlcnMgaXQgaGFzIGl0cyBvd24ga2V5IGZvci4gQ2FjaGVkXG4gKiB7QGxpbmsgT1JHX0FJX0FDQ0VTU19UVExfTVN9LiBUaHJvd3Mge0BsaW5rIEFJQ29uZmlndXJhdGlvbkVycm9yfSAoNTAzKSB3aGVuIHBsYXRmb3JtXG4gKiBjYW5ub3QgYW5zd2VyIOKAlCBzZWUgdGhlIG1vZHVsZSBjb21tZW50IGZvciB3aHkgdGhhdCBpcyBub3QgXCJlbmFibGVkXCIuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaE9yZ0FJQWNjZXNzKG9yZ0lkOiBzdHJpbmcpOiBQcm9taXNlPE9yZ0FJQWNjZXNzPiB7XG4gIGNvbnN0IGhpdCA9IGFjY2Vzc0NhY2hlLmdldChvcmdJZCk7XG4gIGlmIChoaXQgJiYgaGl0LmV4cGlyZXNBdCA+IERhdGUubm93KCkpIHJldHVybiBoaXQudmFsdWU7XG5cbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBwbGF0Zm9ybSgpLmdldDx7IGRhdGE/OiBQYXJ0aWFsPE9yZ0FJQWNjZXNzPiB9ICYgUGFydGlhbDxPcmdBSUFjY2Vzcz4+KFxuICAgIGAke09SR19BSV9BQ0NFU1NfUEFUSH0vJHtlbmNvZGVVUklDb21wb25lbnQob3JnSWQpfWAsIHsgaGVhZGVyczogYXV0aEZvcihvcmdJZCkgfSxcbiAgKTtcbiAgY29uc3QgYm9keSA9IHJlc3BvbnNlPy5ib2R5O1xuICBjb25zdCBkYXRhID0gYm9keT8uZGF0YSA/PyBib2R5O1xuICBpZiAoIXJlc3BvbnNlIHx8IHJlc3BvbnNlLnN0YXR1c0NvZGUgIT09IDIwMCB8fCB0eXBlb2YgZGF0YT8uYWlFbmFibGVkICE9PSAnYm9vbGVhbicpIHtcbiAgICB0aHJvdyBuZXcgQUlDb25maWd1cmF0aW9uRXJyb3IoJ1RoZSBvcmdhbml6YXRpb25cXCdzIEFJIHNldHRpbmdzIGNvdWxkIG5vdCBiZSByZWFkLCBzbyBubyBBSSByZXF1ZXN0IHdhcyBtYWRlLiBUcnkgYWdhaW4gc2hvcnRseS4nKTtcbiAgfVxuICBjb25zdCB2YWx1ZTogT3JnQUlBY2Nlc3MgPSB7XG4gICAgYWlFbmFibGVkOiBkYXRhLmFpRW5hYmxlZCxcbiAgICBwcm92aWRlcnM6IEFycmF5LmlzQXJyYXkoZGF0YS5wcm92aWRlcnMpID8gZGF0YS5wcm92aWRlcnMuZmlsdGVyKChwKTogcCBpcyBzdHJpbmcgPT4gdHlwZW9mIHAgPT09ICdzdHJpbmcnKSA6IFtdLFxuICB9O1xuICBpZiAoT1JHX0FJX0FDQ0VTU19UVExfTVMgPiAwKSBhY2Nlc3NDYWNoZS5zZXQob3JnSWQsIHsgdmFsdWUsIGV4cGlyZXNBdDogRGF0ZS5ub3coKSArIE9SR19BSV9BQ0NFU1NfVFRMX01TIH0pO1xuICByZXR1cm4gdmFsdWU7XG59XG5cbi8qKlxuICogVGhlIG9yZydzIGRlY3J5cHRlZCBrZXkgZm9yIGBwcm92aWRlcmAuIENhbGwgT05MWSBhZnRlciB7QGxpbmsgZmV0Y2hPcmdBSUFjY2Vzc31cbiAqIGxpc3RlZCB0aGUgcHJvdmlkZXIuIFRocm93cyB7QGxpbmsgQUlDb25maWd1cmF0aW9uRXJyb3J9IHdoZW4gdGhlIGtleSBjYW5ub3QgYmVcbiAqIGxvYWRlZCDigJQgdGhlIGNhbGxlciBtdXN0IHRoZW4gZmFpbCB0aGUgcmVxdWVzdCwgbmV2ZXIgZmFsbCBiYWNrIHRvIHRoZSBwbGF0Zm9ybSBrZXkuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBmZXRjaE9yZ0FJS2V5KG9yZ0lkOiBzdHJpbmcsIHByb3ZpZGVyOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICBjb25zdCBjYWNoZUtleSA9IGAke29yZ0lkfVxcdTAwMDAke3Byb3ZpZGVyfWA7XG4gIGNvbnN0IGhpdCA9IGtleUNhY2hlLmdldChjYWNoZUtleSk7XG4gIGlmIChoaXQgJiYgaGl0LmV4cGlyZXNBdCA+IERhdGUubm93KCkpIHJldHVybiBoaXQudmFsdWU7XG5cbiAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBwbGF0Zm9ybSgpLmdldDx7IGRhdGE/OiB7IGFwaUtleT86IHVua25vd24gfTsgYXBpS2V5PzogdW5rbm93biB9PihcbiAgICBgJHtPUkdfQUlfS0VZU19QQVRIfS8ke2VuY29kZVVSSUNvbXBvbmVudChvcmdJZCl9LyR7ZW5jb2RlVVJJQ29tcG9uZW50KHByb3ZpZGVyKX1gLCB7IGhlYWRlcnM6IGF1dGhGb3Iob3JnSWQpIH0sXG4gICk7XG4gIGNvbnN0IGFwaUtleSA9IHJlc3BvbnNlPy5ib2R5Py5kYXRhPy5hcGlLZXkgPz8gcmVzcG9uc2U/LmJvZHk/LmFwaUtleTtcbiAgaWYgKCFyZXNwb25zZSB8fCByZXNwb25zZS5zdGF0dXNDb2RlICE9PSAyMDAgfHwgdHlwZW9mIGFwaUtleSAhPT0gJ3N0cmluZycgfHwgYXBpS2V5Lmxlbmd0aCA9PT0gMCkge1xuICAgIC8vIFN0YXR1cyBvbmx5OiB0aGUgYm9keSBpcyBuZXZlciBlY2hvZWQsIGFuZCB0aGUga2V5IGlzIG5ldmVyIGluIGEgbWVzc2FnZS5cbiAgICB0aHJvdyBuZXcgQUlDb25maWd1cmF0aW9uRXJyb3IoXG4gICAgICBgVGhlIG9yZ2FuaXphdGlvbidzICR7cHJvdmlkZXJ9IEFQSSBrZXkgY291bGQgbm90IGJlIGxvYWRlZCwgc28gdGhlIHJlcXVlc3Qgd2FzIG5vdCBzZW50LiBgXG4gICAgICArICdJdCB3YXMgTk9UIHJldHJpZWQgb24gdGhlIHBsYXRmb3JtXFwncyBrZXkg4oCUIHJlLXNhdmUgdGhlIGtleSBpbiBTZXR0aW5ncywgb3IgdHJ5IGFnYWluIHNob3J0bHkuJyxcbiAgICApO1xuICB9XG4gIGlmIChPUkdfQUlfS0VZX1RUTF9NUyA+IDApIGtleUNhY2hlLnNldChjYWNoZUtleSwgeyB2YWx1ZTogYXBpS2V5LCBleHBpcmVzQXQ6IERhdGUubm93KCkgKyBPUkdfQUlfS0VZX1RUTF9NUyB9KTtcbiAgcmV0dXJuIGFwaUtleTtcbn1cblxuLyoqIFRocm93IHtAbGluayBBSURpc2FibGVkRXJyb3J9ICg0MDMgYEFJX0RJU0FCTEVEYCkgd2hlbiB0aGUgb3JnIHN3aXRjaGVkIEFJIG9mZi4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBhc3NlcnRPcmdBSUVuYWJsZWQob3JnSWQ6IHN0cmluZyk6IFByb21pc2U8T3JnQUlBY2Nlc3M+IHtcbiAgY29uc3QgYWNjZXNzID0gYXdhaXQgZmV0Y2hPcmdBSUFjY2VzcyhvcmdJZCk7XG4gIGlmICghYWNjZXNzLmFpRW5hYmxlZCkgdGhyb3cgbmV3IEFJRGlzYWJsZWRFcnJvcigpO1xuICByZXR1cm4gYWNjZXNzO1xufVxuXG4vKipcbiAqIFJvdXRlIGd1YXJkOiByZWZ1c2UgZXZlcnkgQUkgZW50cnkgcG9pbnQgZm9yIGFuIG9yZyB3aG9zZSBhZG1pbmlzdHJhdG9ycyBzd2l0Y2hlZFxuICogQUkgb2ZmICg0MDMgYEFJX0RJU0FCTEVEYCksIG9yIHdob3NlIHNldHRpbmdzIHBsYXRmb3JtIGNhbm5vdCBjb25maXJtICg1MDMpLiBNb3VudFxuICogQUZURVIgYXV0aCBhbmQgQkVGT1JFIGFueSBxdW90YSByZXNlcnZhdGlvbiwgc28gYSBkaXNhYmxlZCBvcmcgaXMgbmV2ZXIgY2hhcmdlZC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlcXVpcmVPcmdBSUVuYWJsZWQocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCBuZXh0OiBOZXh0RnVuY3Rpb24pOiBQcm9taXNlPHZvaWQ+IHtcbiAgY29uc3Qgb3JnSWQgPSByZXEudXNlcj8ub3JnYW5pemF0aW9uSWQ7XG4gIGlmICghb3JnSWQpIHtcbiAgICBzZW5kRXJyb3IocmVzLCA0MDMsICdObyBvcmdhbml6YXRpb24gYXNzb2NpYXRlZCB3aXRoIHRoaXMgcmVxdWVzdCcsIEVycm9yQ29kZS5JTlNVRkZJQ0lFTlRfUEVSTUlTU0lPTlMpO1xuICAgIHJldHVybjtcbiAgfVxuICB0cnkge1xuICAgIGF3YWl0IGFzc2VydE9yZ0FJRW5hYmxlZChTdHJpbmcob3JnSWQpKTtcbiAgICBuZXh0KCk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIGNvbnN0IGUgPSBlcnIgYXMgQUlEaXNhYmxlZEVycm9yIHwgQUlDb25maWd1cmF0aW9uRXJyb3I7XG4gICAgc2VuZEVycm9yKHJlcywgZS5zdGF0dXNDb2RlID8/IDUwMywgZS5tZXNzYWdlLCBlLmNvZGUpO1xuICB9XG59XG5cbi8qKiBEcm9wIHRoZSBjYWNoZWQgb3JnIEFJIHBvc3R1cmUgKyBrZXlzICh0ZXN0czsgYW4gb3JnIHdob3NlIHNldHRpbmdzIGp1c3QgY2hhbmdlZCkuICovXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRPcmdBSUNhY2hlKG9yZ0lkPzogc3RyaW5nKTogdm9pZCB7XG4gIGlmIChvcmdJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgYWNjZXNzQ2FjaGUuY2xlYXIoKTtcbiAgICBrZXlDYWNoZS5jbGVhcigpO1xuICAgIHJldHVybjtcbiAgfVxuICBhY2Nlc3NDYWNoZS5kZWxldGUob3JnSWQpO1xuICBmb3IgKGNvbnN0IGsgb2Yga2V5Q2FjaGUua2V5cygpKSBpZiAoay5zdGFydHNXaXRoKGAke29yZ0lkfVxcdTAwMDBgKSkga2V5Q2FjaGUuZGVsZXRlKGspO1xufVxuIl19
@@ -0,0 +1,101 @@
1
+ export declare const PAGERDUTY_API_URL = "https://api.pagerduty.com";
2
+ /**
3
+ * Verify a PagerDuty v3 webhook signature.
4
+ *
5
+ * The scheme: `X-PagerDuty-Signature: v1=<lowercase hex>`, an HMAC-SHA256 over
6
+ * the RAW request body. Three properties of it are easy to get wrong and each
7
+ * one is a hole:
8
+ *
9
+ * - THE RAW BODY, not a re-serialized object. `JSON.stringify(req.body)` after
10
+ * a JSON parser has run produces different bytes (key order, whitespace,
11
+ * number formatting), so every signature fails and the obvious "fix" is to
12
+ * stop checking. The route therefore mounts `express.raw`.
13
+ * - MULTIPLE SIGNATURES. The header may carry several comma-separated `v1=`
14
+ * values, which is how PagerDuty rotates a secret without downtime. Checking
15
+ * only the first breaks during their rotation, so ANY match is accepted —
16
+ * the same overlap this repo already runs for its own relay token.
17
+ * - CONSTANT TIME. Compared with `timingSafeEqual` on equal-length buffers,
18
+ * because a byte-at-a-time comparison leaks the expected digest to anyone
19
+ * who can post here repeatedly.
20
+ *
21
+ * NO SECRET MEANS NO. An unverified webhook that moves an audit record and a
22
+ * metric is a way for anyone who learns the URL to assert that a page was
23
+ * handled — the one lie that would make the whole feature report coverage it
24
+ * does not have. Fails closed.
25
+ */
26
+ export declare function verifyPagerDutySignature(rawBody: Buffer | string, header: string | undefined, secret: string): boolean;
27
+ export interface OnCallInboundConfig {
28
+ webhookSecret: string;
29
+ apiToken: string;
30
+ apiUrl: string;
31
+ /** How far ahead the rota check looks. The gap alert is only as useful as this. */
32
+ lookaheadHours: number;
33
+ /** Escalation policy ids to check coverage for; empty means the whole account. */
34
+ escalationPolicyIds: string[];
35
+ timeoutMs: number;
36
+ }
37
+ export declare function onCallInboundConfig(): OnCallInboundConfig;
38
+ export interface RotaCoverage {
39
+ /** Whether the question could be answered at all. */
40
+ known: boolean;
41
+ /** Distinct on-call entries covering any part of the window. */
42
+ entries: number;
43
+ /** Seconds of the lookahead window with nobody on call, 0 when fully covered. */
44
+ uncoveredSeconds: number;
45
+ }
46
+ /**
47
+ * Ask the provider whether anyone is on call across the lookahead window.
48
+ *
49
+ * WHAT THIS CAN AND CANNOT CONCLUDE, because the difference decides whether the
50
+ * alert built on it is honest. `GET /oncalls?since&until` returns the on-call
51
+ * entries OVERLAPPING a window; it does not return the holes. Reconstructing
52
+ * exact holes would mean stitching every entry's interval, and an entry's
53
+ * `start`/`end` are null for an always-on policy — so a precise
54
+ * "uncovered seconds" is not available from this endpoint without a lot of
55
+ * arithmetic that would be wrong in the always-on case.
56
+ *
57
+ * So this answers the question it CAN answer exactly: is the window covered at
58
+ * all. Zero entries over the next N hours means nobody is scheduled, which is
59
+ * the failure worth paging about and the one a rota maintainer actually makes.
60
+ * `uncoveredSeconds` is therefore the whole window or nothing, and it is a
61
+ * deliberate under-report rather than a guess — a partial hole reads as covered
62
+ * here, and claiming otherwise would be inventing precision.
63
+ *
64
+ * `known: false` when there is no API token or the call fails, and the caller
65
+ * must publish NOTHING in that case. A gauge of 0 would say "fully covered",
66
+ * which is exactly the lie this check exists to prevent.
67
+ */
68
+ export declare function fetchRotaCoverage(cfg?: OnCallInboundConfig): Promise<RotaCoverage>;
69
+ /** The webhook events this system acts on. Everything else is counted and dropped. */
70
+ export type OnCallWebhookEventType = 'incident.acknowledged' | 'incident.escalated' | 'incident.resolved' | 'incident.unacknowledged';
71
+ export interface OnCallWebhookEvent {
72
+ eventType: string;
73
+ /** Provider incident id — the join back to what we sent. */
74
+ incidentId?: string;
75
+ /** Our own `dedup_key`, when the provider echoes it. */
76
+ dedupKey?: string;
77
+ /** Who acted, as the provider knows them. An id and an email, never a session. */
78
+ agentId?: string;
79
+ agentSummary?: string;
80
+ /** When the incident was created, for the ack-latency measurement. */
81
+ createdAt?: string;
82
+ occurredAt?: string;
83
+ }
84
+ /**
85
+ * Reduce a v3 webhook body to the fields this system uses.
86
+ *
87
+ * Returns `null` for anything that is not a recognised incident event, which
88
+ * the caller COUNTS rather than drops silently — an unrecognised event type is
89
+ * usually a subscription configured wider than the handler, and a count is how
90
+ * that becomes visible instead of being inferred from nothing happening.
91
+ */
92
+ export declare function parseOnCallWebhook(body: unknown): OnCallWebhookEvent | null;
93
+ /**
94
+ * Seconds between the incident being raised and this event.
95
+ *
96
+ * Returns `undefined` rather than 0 when either timestamp is missing or the
97
+ * arithmetic is nonsense: a 0 would land in the first histogram bucket and read
98
+ * as an instant acknowledgement, which is the most flattering possible lie
99
+ * about response time.
100
+ */
101
+ export declare function ackLatencySeconds(event: OnCallWebhookEvent): number | undefined;