@agent-native/core 0.91.2 → 0.92.0
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/docs/content/faq.mdx +17 -0
- package/corpus/core/docs/content/notifications.mdx +12 -6
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +140 -46
- package/corpus/core/src/agent/tool-search.ts +71 -4
- package/corpus/core/src/cli/index.ts +20 -0
- package/corpus/core/src/cli/upgrade.ts +798 -0
- package/corpus/core/src/client/AssistantChat.tsx +1 -1
- package/corpus/core/src/client/HighlightedCodeBlock.tsx +20 -3
- package/corpus/core/src/client/agent-chat-adapter.ts +31 -6
- package/corpus/core/src/client/chat/message-components.tsx +85 -8
- package/corpus/core/src/client/chat/tool-call-display.tsx +355 -144
- package/corpus/core/src/client/code-agent-chat-adapter.ts +2 -9
- package/corpus/core/src/client/session-replay.ts +3 -97
- package/corpus/core/src/client/settings/SettingsPanel.tsx +533 -389
- package/corpus/core/src/client/settings/SettingsSection.tsx +154 -17
- package/corpus/core/src/client/sse-event-processor.ts +29 -2
- package/corpus/core/src/extensions/actions.ts +113 -5
- package/corpus/core/src/notifications/channels.ts +88 -25
- package/corpus/core/src/notifications/registry.ts +17 -3
- package/corpus/core/src/notifications/types.ts +4 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +7 -3
- package/corpus/core/src/server/request-context.ts +7 -0
- package/corpus/core/src/styles/agent-conversation.css +1 -1
- package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/core/src/templates/default/AGENTS.md +10 -3
- package/corpus/core/src/templates/default/package.json +2 -1
- package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/core/src/templates/headless/AGENTS.md +9 -3
- package/corpus/core/src/templates/headless/package.json +1 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/core/src/templates/workspace-root/AGENTS.md +10 -4
- package/corpus/core/src/templates/workspace-root/package.json +1 -0
- package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +5 -6
- package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/analytics/AGENTS.md +7 -5
- package/corpus/templates/analytics/actions/save-monitor.ts +14 -0
- package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +41 -6
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +122 -11
- package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +8 -2
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +151 -32
- package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +17 -2
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +4 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +54 -190
- package/corpus/templates/analytics/changelog/2026-07-08-dashboard-tables-keep-their-size-while-loading.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-monitoring-uptime-and-errors-tabs-now-clearly-show-which-view-is-active.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-session-replay-uses-stock-rrweb-sizing-again-so-recorded.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-sidebar-dashboard-menus-now-open-above-the-dashboard-canvas.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-08-uptime-alerting-clarifies-in-app-inbox-vs-email-and-lets-you-paste-slack-or-webhook-urls-per-monitor.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +13 -11
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +4 -0
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +60 -12
- package/corpus/templates/analytics/server/plugins/db.ts +10 -0
- package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-notes-stay-active-from-tray-and-shortcut-controls.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-popovers-can-start-notes-and-join-the-meetin.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-meeting-notes-keep-their-recording-state-when-the-desktop.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-08-meeting-reminder-dropdowns-now-expand-fully-instead-of-being.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +123 -3
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +15 -1
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -0
- package/corpus/templates/clips/desktop/src/styles.css +23 -1
- package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +1 -3
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +40 -6
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -2
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +3 -5
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +23 -19
- package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +3 -3
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +0 -1
- package/corpus/templates/clips/desktop/src-tauri/src/state.rs +5 -0
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -6
- package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/content/actions/_batch-utils.ts +8 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +7 -5
- package/corpus/templates/content/actions/_database-source-utils.ts +776 -319
- package/corpus/templates/content/actions/_database-utils.ts +6 -1
- package/corpus/templates/content/actions/_property-utils.ts +168 -0
- package/corpus/templates/content/actions/add-content-database-source-field-property.ts +19 -12
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +19 -12
- package/corpus/templates/content/actions/delete-document.ts +9 -13
- package/corpus/templates/content/actions/refresh-content-database-source.ts +5 -5
- package/corpus/templates/content/app/components/editor/body-hydration.ts +35 -11
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +47 -5
- package/corpus/templates/content/app/hooks/use-documents.ts +8 -2
- package/corpus/templates/content/changelog/2026-07-08-builder-source-sync-now-completes-reliably-on-hosted-databases.md +6 -0
- package/corpus/templates/content/changelog/2026-07-08-fixed-builder-database-sync-so-large-sources-continue-past-t.md +6 -0
- package/corpus/templates/content/server/plugins/db.ts +19 -0
- package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/videos/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/corpus/templates/voice/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/agent/production-agent.d.ts +5 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +112 -42
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/tool-search.d.ts +2 -0
- package/dist/agent/tool-search.d.ts.map +1 -1
- package/dist/agent/tool-search.js +60 -4
- package/dist/agent/tool-search.js.map +1 -1
- package/dist/cli/index.js +19 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/upgrade.d.ts +100 -0
- package/dist/cli/upgrade.d.ts.map +1 -0
- package/dist/cli/upgrade.js +678 -0
- package/dist/cli/upgrade.js.map +1 -0
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/HighlightedCodeBlock.d.ts +2 -0
- package/dist/client/HighlightedCodeBlock.d.ts.map +1 -1
- package/dist/client/HighlightedCodeBlock.js +10 -1
- package/dist/client/HighlightedCodeBlock.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +32 -4
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +61 -8
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +12 -0
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +136 -42
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +2 -9
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/session-replay.d.ts +0 -8
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +3 -80
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +52 -19
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/SettingsSection.d.ts +19 -4
- package/dist/client/settings/SettingsSection.d.ts.map +1 -1
- package/dist/client/settings/SettingsSection.js +33 -4
- package/dist/client/settings/SettingsSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +9 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +20 -2
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +85 -5
- package/dist/extensions/actions.js.map +1 -1
- package/dist/notifications/channels.d.ts +8 -3
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +61 -22
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +14 -3
- package/dist/notifications/registry.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/notifications/types.d.ts +4 -1
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/notifications/types.js.map +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts +5 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +2 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/request-context.d.ts +7 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/styles/agent-conversation.css +1 -1
- package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/templates/default/AGENTS.md +10 -3
- package/dist/templates/default/package.json +2 -1
- package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/templates/headless/AGENTS.md +9 -3
- package/dist/templates/headless/package.json +1 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/dist/templates/workspace-root/AGENTS.md +10 -4
- package/dist/templates/workspace-root/package.json +1 -0
- package/docs/content/faq.mdx +17 -0
- package/docs/content/notifications.mdx +12 -6
- package/package.json +2 -2
- package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/src/templates/default/AGENTS.md +10 -3
- package/src/templates/default/package.json +2 -1
- package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/src/templates/headless/AGENTS.md +9 -3
- package/src/templates/headless/package.json +1 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
- package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
- package/src/templates/workspace-root/AGENTS.md +10 -4
- package/src/templates/workspace-root/package.json +1 -0
- package/corpus/templates/analytics/changelog/2026-07-08-session-replay-clamps-ultra-wide-viewports-while-keeping-the.md +0 -6
|
@@ -117,7 +117,7 @@ const MESSAGES = {
|
|
|
117
117
|
sectionResponseChecksHint:
|
|
118
118
|
"Choose which responses count as healthy and add body, header, or latency assertions.",
|
|
119
119
|
sectionAlerting: "Alerting",
|
|
120
|
-
sectionAlertingHint: "
|
|
120
|
+
sectionAlertingHint: "Choose severity and where alerts go.",
|
|
121
121
|
sectionAdvanced: "Advanced",
|
|
122
122
|
sectionAdvancedHint:
|
|
123
123
|
"Request method, timeout, cooldown, redirects, and pause.",
|
|
@@ -165,12 +165,24 @@ const MESSAGES = {
|
|
|
165
165
|
severityWarning: "Warning",
|
|
166
166
|
severityCritical: "Critical",
|
|
167
167
|
fieldChannels: "Notify via",
|
|
168
|
-
channelInbox: "
|
|
168
|
+
channelInbox: "In-app inbox",
|
|
169
|
+
channelInboxHint: "Bell notifications inside Analytics.",
|
|
169
170
|
channelEmail: "Email",
|
|
171
|
+
channelEmailHint: "Send to specific addresses.",
|
|
170
172
|
channelSlack: "Slack",
|
|
173
|
+
channelSlackHint: "Post to a Slack incoming webhook.",
|
|
171
174
|
channelWebhook: "Webhook",
|
|
175
|
+
channelWebhookHint: "POST JSON to any HTTPS endpoint.",
|
|
172
176
|
fieldEmailRecipients: "Email recipients",
|
|
173
177
|
fieldEmailRecipientsPlaceholder: "alerts@example.com, oncall@example.com",
|
|
178
|
+
fieldSlackWebhookUrl: "Slack webhook URL",
|
|
179
|
+
fieldSlackWebhookUrlPlaceholder: "https://hooks.slack.com/services/T…/B…/…",
|
|
180
|
+
fieldSlackWebhookUrlHint:
|
|
181
|
+
"Paste a Slack Incoming Webhook URL. Leave blank to use the workspace default, if configured.",
|
|
182
|
+
fieldWebhookUrl: "Webhook URL",
|
|
183
|
+
fieldWebhookUrlPlaceholder: "https://example.com/hooks/uptime",
|
|
184
|
+
fieldWebhookUrlHint:
|
|
185
|
+
"Receives a JSON POST with severity, title, and body. Leave blank to use the workspace default, if configured.",
|
|
174
186
|
fieldCooldown: "Alert cooldown (minutes)",
|
|
175
187
|
fieldEnabled: "Enabled",
|
|
176
188
|
fieldEnabledHint: "Run this monitor on its schedule.",
|
|
@@ -188,6 +200,9 @@ const MESSAGES = {
|
|
|
188
200
|
urlRequired: "Enter an absolute http(s) URL.",
|
|
189
201
|
urlInvalid: "Enter a valid http(s) URL (e.g. https://example.com).",
|
|
190
202
|
channelRequired: "Select at least one alert channel.",
|
|
203
|
+
emailRecipientsRequired: "Add at least one email recipient.",
|
|
204
|
+
slackWebhookUrlInvalid: "Enter a valid Slack webhook http(s) URL.",
|
|
205
|
+
webhookUrlInvalid: "Enter a valid webhook http(s) URL.",
|
|
191
206
|
classRequired: "Select at least one status class.",
|
|
192
207
|
codesRequired: "Enter at least one status code.",
|
|
193
208
|
saved: "Monitor saved.",
|
|
@@ -61,6 +61,8 @@ export interface MonitorSummary {
|
|
|
61
61
|
severity: MonitorSeverity;
|
|
62
62
|
channels: string[];
|
|
63
63
|
emailRecipients: string[];
|
|
64
|
+
slackWebhookUrl: string | null;
|
|
65
|
+
webhookUrl: string | null;
|
|
64
66
|
cooldownMinutes: number;
|
|
65
67
|
enabled: boolean;
|
|
66
68
|
lastStatus: MonitorStatus | null;
|
|
@@ -156,6 +158,8 @@ export interface SaveMonitorInput {
|
|
|
156
158
|
severity?: MonitorSeverity;
|
|
157
159
|
channels?: string[];
|
|
158
160
|
emailRecipients?: string[];
|
|
161
|
+
slackWebhookUrl?: string | null;
|
|
162
|
+
webhookUrl?: string | null;
|
|
159
163
|
cooldownMinutes?: number;
|
|
160
164
|
enabled?: boolean;
|
|
161
165
|
}
|
|
@@ -156,6 +156,8 @@ type ReplayViewportDimensions = {
|
|
|
156
156
|
|
|
157
157
|
const DEFAULT_PLAYER_WIDTH = 1024;
|
|
158
158
|
const DEFAULT_PLAYER_HEIGHT = 640;
|
|
159
|
+
const MIN_REPLAY_DISPLAY_ASPECT_RATIO = 0.45;
|
|
160
|
+
const MAX_REPLAY_DISPLAY_ASPECT_RATIO = 3;
|
|
159
161
|
const DEFAULT_SPEED = 2;
|
|
160
162
|
const SPEED_OPTIONS = [0.5, 1, 2, 4, 8];
|
|
161
163
|
const SKIP_STEP_MS = 5000;
|
|
@@ -166,15 +168,12 @@ const REPLAY_CHUNK_UNAVAILABLE_MESSAGE = "Session replay chunk is unavailable";
|
|
|
166
168
|
const DEFAULT_DEVTOOLS_HEIGHT = 220;
|
|
167
169
|
const MIN_STAGE_HEIGHT_PX = 240;
|
|
168
170
|
const SCRUBBER_MARKER_LIMIT = 500;
|
|
169
|
-
/** Reject Meta/resize frames that produce the ultra-wide ribbon letterbox.
|
|
170
|
-
* True 21:9 ultrawide is ~2.33; multi-monitor spans and corrupt frames are
|
|
171
|
-
* usually well above that and collapse the stage after fit-to-scale. */
|
|
172
|
-
const MIN_REPLAY_VIEWPORT_WIDTH = 320;
|
|
173
|
-
const MIN_REPLAY_VIEWPORT_HEIGHT = 240;
|
|
174
|
-
const MAX_REPLAY_ASPECT_RATIO = 2.45;
|
|
175
|
-
const MIN_REPLAY_ASPECT_RATIO = 0.5;
|
|
176
171
|
const TIMELINE_MARKER_LIMIT = 300;
|
|
177
172
|
const TIMELINE_FOLLOW_PAUSE_MS = 4000;
|
|
173
|
+
/** Toast/snackbar noise only — keep insertStyleRules minimal like builder-internal. */
|
|
174
|
+
const SUPPRESS_OVERLAYS_CSS = [
|
|
175
|
+
"[data-radix-popper-content-wrapper], .Toastify, [class*='toast'], [class*='Toast'], [class*='Snackbar'] { display: none !important; }",
|
|
176
|
+
];
|
|
178
177
|
const SESSION_REPLAY_CONSOLE_EVENT_TAG = "agent-native.console";
|
|
179
178
|
const SESSION_REPLAY_NETWORK_EVENT_TAG = "agent-native.network";
|
|
180
179
|
|
|
@@ -506,10 +505,9 @@ function ReplayPlayer({
|
|
|
506
505
|
const scrubbingRef = useRef(false);
|
|
507
506
|
const scrubResumePlayingRef = useRef(false);
|
|
508
507
|
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
const playerHeight =
|
|
512
|
-
streamedDims?.height ?? initialDims?.height ?? DEFAULT_PLAYER_HEIGHT;
|
|
508
|
+
const displayDims = clampReplayDisplayDimensions(streamedDims ?? initialDims);
|
|
509
|
+
const playerWidth = displayDims?.width ?? DEFAULT_PLAYER_WIDTH;
|
|
510
|
+
const playerHeight = displayDims?.height ?? DEFAULT_PLAYER_HEIGHT;
|
|
513
511
|
const skipRanges = useMemo(() => buildIdleSkipRanges(events), [events]);
|
|
514
512
|
const skipRangesRef = useLiveRef(skipRanges);
|
|
515
513
|
const skipInactiveRef = useLiveRef(skipInactive);
|
|
@@ -695,33 +693,21 @@ function ReplayPlayer({
|
|
|
695
693
|
if (cancelled || !stageRootRef.current) return;
|
|
696
694
|
|
|
697
695
|
stageRootRef.current.innerHTML = "";
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
);
|
|
706
|
-
setStreamedDims(frameDims);
|
|
707
|
-
localReplayer = new Replayer(playbackEvents as any[], {
|
|
696
|
+
// Match builder-internal: pass events through untouched and let rrweb own
|
|
697
|
+
// iframe sizing via Meta / ViewportResize. Only use dimensions for CSS
|
|
698
|
+
// fit-to-stage scaling of the outer wrapper — never rewrite Meta or force
|
|
699
|
+
// iframe width/height (that mismatches the FullSnapshot DOM and blanks
|
|
700
|
+
// the stage).
|
|
701
|
+
setStreamedDims(replayViewportDimensions(replayEvents));
|
|
702
|
+
localReplayer = new Replayer(replayEvents as any[], {
|
|
708
703
|
root: stageRootRef.current,
|
|
709
704
|
speed: speedRef.current,
|
|
710
705
|
skipInactive: false,
|
|
711
706
|
showWarning: false,
|
|
712
707
|
showDebug: false,
|
|
713
708
|
triggerFocus: false,
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
mouseTail: {
|
|
717
|
-
strokeStyle: "rgba(59, 130, 246, 0.35)",
|
|
718
|
-
lineWidth: 2,
|
|
719
|
-
duration: 600,
|
|
720
|
-
lineCap: "round",
|
|
721
|
-
},
|
|
722
|
-
insertStyleRules: [
|
|
723
|
-
"[data-radix-popper-content-wrapper], .Toastify, [class*='toast'], [class*='Toast'] { display: none !important; }",
|
|
724
|
-
],
|
|
709
|
+
mouseTail: false,
|
|
710
|
+
insertStyleRules: SUPPRESS_OVERLAYS_CSS,
|
|
725
711
|
});
|
|
726
712
|
replayerRef.current = localReplayer;
|
|
727
713
|
const meta = localReplayer.getMetaData?.();
|
|
@@ -732,7 +718,6 @@ function ReplayPlayer({
|
|
|
732
718
|
0,
|
|
733
719
|
Number.isFinite(total) ? total : 0,
|
|
734
720
|
);
|
|
735
|
-
applyReplayFrameDimensions(localReplayer, frameDims);
|
|
736
721
|
localReplayer.on?.("finish", () => {
|
|
737
722
|
setPlaying(false);
|
|
738
723
|
const finalTime = Number(localReplayer.getCurrentTime?.() ?? total);
|
|
@@ -740,21 +725,20 @@ function ReplayPlayer({
|
|
|
740
725
|
});
|
|
741
726
|
localReplayer.on?.("resize", (payload: unknown) => {
|
|
742
727
|
const dims = payload as { width?: unknown; height?: unknown };
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
728
|
+
if (
|
|
729
|
+
typeof dims.width === "number" &&
|
|
730
|
+
typeof dims.height === "number" &&
|
|
731
|
+
Number.isFinite(dims.width) &&
|
|
732
|
+
Number.isFinite(dims.height) &&
|
|
733
|
+
dims.width > 0 &&
|
|
734
|
+
dims.height > 0
|
|
735
|
+
) {
|
|
736
|
+
setStreamedDims({
|
|
737
|
+
width: Math.round(dims.width),
|
|
738
|
+
height: Math.round(dims.height),
|
|
739
|
+
});
|
|
748
740
|
}
|
|
749
741
|
});
|
|
750
|
-
localReplayer.on?.("fullsnapshot-rebuilded", () => {
|
|
751
|
-
applyReplayFrameDimensions(
|
|
752
|
-
localReplayer,
|
|
753
|
-
replayViewportAt(eventsRef.current, currentTimeRef.current) ??
|
|
754
|
-
frameDims,
|
|
755
|
-
);
|
|
756
|
-
revealReplayFrame(localReplayer);
|
|
757
|
-
});
|
|
758
742
|
updateTime(startAt);
|
|
759
743
|
setStatus("ready");
|
|
760
744
|
try {
|
|
@@ -769,14 +753,6 @@ function ReplayPlayer({
|
|
|
769
753
|
}
|
|
770
754
|
setPlaying(false);
|
|
771
755
|
}
|
|
772
|
-
window.requestAnimationFrame(() => {
|
|
773
|
-
applyReplayFrameDimensions(
|
|
774
|
-
localReplayer,
|
|
775
|
-
replayViewportAt(eventsRef.current, currentTimeRef.current) ??
|
|
776
|
-
frameDims,
|
|
777
|
-
);
|
|
778
|
-
revealReplayFrame(localReplayer);
|
|
779
|
-
});
|
|
780
756
|
}
|
|
781
757
|
|
|
782
758
|
void loadReplay().catch((loadError: any) => {
|
|
@@ -2177,9 +2153,8 @@ function hasPlayableReplayEvents(events: unknown[]): boolean {
|
|
|
2177
2153
|
export function replayViewportDimensions(
|
|
2178
2154
|
events: AnyReplayEvent[],
|
|
2179
2155
|
): ReplayViewportDimensions | null {
|
|
2180
|
-
//
|
|
2181
|
-
//
|
|
2182
|
-
// the stage into an ultra-wide ribbon after fit-to-stage scaling.
|
|
2156
|
+
// Latest Meta / ViewportResize for CSS fit-to-stage only. Never rewrite these
|
|
2157
|
+
// into the event stream — rrweb must keep Meta in sync with the FullSnapshot.
|
|
2183
2158
|
let best: ReplayViewportDimensions | null = null;
|
|
2184
2159
|
for (const event of events) {
|
|
2185
2160
|
const dims = dimensionsFromReplayEvent(event);
|
|
@@ -2188,22 +2163,6 @@ export function replayViewportDimensions(
|
|
|
2188
2163
|
return best;
|
|
2189
2164
|
}
|
|
2190
2165
|
|
|
2191
|
-
export function replayViewportAt(
|
|
2192
|
-
events: AnyReplayEvent[],
|
|
2193
|
-
currentTime: number,
|
|
2194
|
-
): ReplayViewportDimensions | null {
|
|
2195
|
-
const startedAt = replayStartedAt(events);
|
|
2196
|
-
let current: ReplayViewportDimensions | null = null;
|
|
2197
|
-
for (const event of events) {
|
|
2198
|
-
const dims = dimensionsFromReplayEvent(event);
|
|
2199
|
-
if (!dims) continue;
|
|
2200
|
-
const offset = Number(event.timestamp ?? 0) - startedAt;
|
|
2201
|
-
if (offset <= currentTime + 50) current = dims;
|
|
2202
|
-
else break;
|
|
2203
|
-
}
|
|
2204
|
-
return current ?? replayViewportDimensions(events);
|
|
2205
|
-
}
|
|
2206
|
-
|
|
2207
2166
|
function dimensionsFromReplayEvent(
|
|
2208
2167
|
event: AnyReplayEvent,
|
|
2209
2168
|
): ReplayViewportDimensions | null {
|
|
@@ -2219,12 +2178,7 @@ function dimensionsFromReplayEvent(
|
|
|
2219
2178
|
return null;
|
|
2220
2179
|
}
|
|
2221
2180
|
|
|
2222
|
-
/**
|
|
2223
|
-
* Prefer a sane viewport for the stage. Returns null only when width/height
|
|
2224
|
-
* are missing or non-positive. Out-of-range aspect ratios are clamped rather
|
|
2225
|
-
* than rejected so genuine wide windows keep their height (and most of their
|
|
2226
|
-
* width) instead of collapsing to the default 1024×640 fallback.
|
|
2227
|
-
*/
|
|
2181
|
+
/** Read raw positive finite dimensions; no aspect clamping. */
|
|
2228
2182
|
export function normalizeReplayDimensions(
|
|
2229
2183
|
width: unknown,
|
|
2230
2184
|
height: unknown,
|
|
@@ -2239,28 +2193,30 @@ export function normalizeReplayDimensions(
|
|
|
2239
2193
|
) {
|
|
2240
2194
|
return null;
|
|
2241
2195
|
}
|
|
2242
|
-
return
|
|
2196
|
+
return {
|
|
2197
|
+
width: Math.round(width),
|
|
2198
|
+
height: Math.round(height),
|
|
2199
|
+
};
|
|
2243
2200
|
}
|
|
2244
2201
|
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
nextHeight = Math.round(nextWidth / MIN_REPLAY_ASPECT_RATIO);
|
|
2202
|
+
export function clampReplayDisplayDimensions(
|
|
2203
|
+
dims: ReplayViewportDimensions | null,
|
|
2204
|
+
): ReplayViewportDimensions | null {
|
|
2205
|
+
if (!dims) return null;
|
|
2206
|
+
const aspect = dims.width / dims.height;
|
|
2207
|
+
if (aspect > MAX_REPLAY_DISPLAY_ASPECT_RATIO) {
|
|
2208
|
+
return {
|
|
2209
|
+
width: Math.round(dims.height * MAX_REPLAY_DISPLAY_ASPECT_RATIO),
|
|
2210
|
+
height: dims.height,
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
if (aspect < MIN_REPLAY_DISPLAY_ASPECT_RATIO) {
|
|
2214
|
+
return {
|
|
2215
|
+
width: dims.width,
|
|
2216
|
+
height: Math.round(dims.width / MIN_REPLAY_DISPLAY_ASPECT_RATIO),
|
|
2217
|
+
};
|
|
2262
2218
|
}
|
|
2263
|
-
return
|
|
2219
|
+
return dims;
|
|
2264
2220
|
}
|
|
2265
2221
|
|
|
2266
2222
|
export function filterReplayMarkers(
|
|
@@ -2284,98 +2240,6 @@ export function filterReplayMarkers(
|
|
|
2284
2240
|
});
|
|
2285
2241
|
}
|
|
2286
2242
|
|
|
2287
|
-
/**
|
|
2288
|
-
* Rewrite Meta / ViewportResize frames so rrweb never applies absurd aspect
|
|
2289
|
-
* ratios that collapse the stage into an ultra-wide ribbon. Prefer clamping
|
|
2290
|
-
* the captured dimensions (preserve height, shrink width) over replacing them
|
|
2291
|
-
* with the default fallback, which would misalign the recorded DOM.
|
|
2292
|
-
*/
|
|
2293
|
-
export function sanitizeReplayViewportEvents(
|
|
2294
|
-
events: AnyReplayEvent[],
|
|
2295
|
-
fallback: ReplayViewportDimensions,
|
|
2296
|
-
): AnyReplayEvent[] {
|
|
2297
|
-
return events.map((event) => {
|
|
2298
|
-
if (event.type === RRWEB_EVENT_TYPE.Meta && isRecord(event.data)) {
|
|
2299
|
-
return rewriteViewportEventData(event, event.data, fallback);
|
|
2300
|
-
}
|
|
2301
|
-
if (
|
|
2302
|
-
event.type === RRWEB_EVENT_TYPE.IncrementalSnapshot &&
|
|
2303
|
-
event.data?.source === INCREMENTAL_SOURCE.ViewportResize &&
|
|
2304
|
-
isRecord(event.data)
|
|
2305
|
-
) {
|
|
2306
|
-
return rewriteViewportEventData(event, event.data, fallback);
|
|
2307
|
-
}
|
|
2308
|
-
return event;
|
|
2309
|
-
});
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
function rewriteViewportEventData(
|
|
2313
|
-
event: AnyReplayEvent,
|
|
2314
|
-
data: AnyRecord,
|
|
2315
|
-
fallback: ReplayViewportDimensions,
|
|
2316
|
-
): AnyReplayEvent {
|
|
2317
|
-
const width = data.width;
|
|
2318
|
-
const height = data.height;
|
|
2319
|
-
if (
|
|
2320
|
-
typeof width === "number" &&
|
|
2321
|
-
typeof height === "number" &&
|
|
2322
|
-
Number.isFinite(width) &&
|
|
2323
|
-
Number.isFinite(height) &&
|
|
2324
|
-
width > 0 &&
|
|
2325
|
-
height > 0
|
|
2326
|
-
) {
|
|
2327
|
-
const clamped = clampReplayViewport(width, height);
|
|
2328
|
-
if (
|
|
2329
|
-
clamped.width === Math.round(width) &&
|
|
2330
|
-
clamped.height === Math.round(height)
|
|
2331
|
-
) {
|
|
2332
|
-
return event;
|
|
2333
|
-
}
|
|
2334
|
-
return {
|
|
2335
|
-
...event,
|
|
2336
|
-
data: {
|
|
2337
|
-
...data,
|
|
2338
|
-
width: clamped.width,
|
|
2339
|
-
height: clamped.height,
|
|
2340
|
-
},
|
|
2341
|
-
};
|
|
2342
|
-
}
|
|
2343
|
-
return {
|
|
2344
|
-
...event,
|
|
2345
|
-
data: {
|
|
2346
|
-
...data,
|
|
2347
|
-
width: fallback.width,
|
|
2348
|
-
height: fallback.height,
|
|
2349
|
-
},
|
|
2350
|
-
};
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
function applyReplayFrameDimensions(
|
|
2354
|
-
replayer: any,
|
|
2355
|
-
dims: ReplayViewportDimensions | null,
|
|
2356
|
-
) {
|
|
2357
|
-
const width = dims?.width ?? DEFAULT_PLAYER_WIDTH;
|
|
2358
|
-
const height = dims?.height ?? DEFAULT_PLAYER_HEIGHT;
|
|
2359
|
-
const wrapper = replayer?.wrapper as HTMLElement | undefined;
|
|
2360
|
-
const iframe = replayer?.iframe as HTMLIFrameElement | undefined;
|
|
2361
|
-
const mouseTail = replayer?.mouseTail as HTMLCanvasElement | undefined;
|
|
2362
|
-
for (const element of [wrapper, iframe]) {
|
|
2363
|
-
if (!element) continue;
|
|
2364
|
-
element.style.width = `${width}px`;
|
|
2365
|
-
element.style.height = `${height}px`;
|
|
2366
|
-
}
|
|
2367
|
-
for (const element of [iframe, mouseTail]) {
|
|
2368
|
-
if (!element) continue;
|
|
2369
|
-
element.setAttribute("width", String(width));
|
|
2370
|
-
element.setAttribute("height", String(height));
|
|
2371
|
-
}
|
|
2372
|
-
}
|
|
2373
|
-
|
|
2374
|
-
function revealReplayFrame(replayer: any) {
|
|
2375
|
-
const iframe = replayer?.iframe as HTMLIFrameElement | undefined;
|
|
2376
|
-
if (iframe) iframe.style.display = "inherit";
|
|
2377
|
-
}
|
|
2378
|
-
|
|
2379
2243
|
function replayStartedAt(events: AnyReplayEvent[]): number {
|
|
2380
2244
|
const first = events.find((event) =>
|
|
2381
2245
|
Number.isFinite(Number(event.timestamp)),
|
|
@@ -60,10 +60,12 @@ Each check runs on the server (`server/lib/uptime-monitors.ts`):
|
|
|
60
60
|
|
|
61
61
|
- On a transition **into failure**, it opens a `monitor_incidents` row (one per
|
|
62
62
|
continuous failure streak) and calls `notifyWithDelivery` with a clear title
|
|
63
|
-
and body plus metadata
|
|
64
|
-
`{ kind: "uptime_monitor", monitorId, url, statusCode, latencyMs, failedAssertions }`.
|
|
65
|
-
The `inbox` channel is
|
|
66
|
-
|
|
63
|
+
and body plus safe inbox metadata
|
|
64
|
+
`{ kind: "uptime_monitor", monitorId, url, statusCode, latencyMs, failedAssertions, emailRecipients }`.
|
|
65
|
+
The `inbox` channel is the in-app bell; `email` uses the monitor's
|
|
66
|
+
`emailRecipients`; `slack` / `webhook` use the monitor's optional
|
|
67
|
+
delivery-only `slackWebhookUrl` / `webhookUrl` (falling back to workspace
|
|
68
|
+
`NOTIFICATIONS_SLACK_WEBHOOK_URL` / `NOTIFICATIONS_WEBHOOK_URL` when unset).
|
|
67
69
|
- While an incident is open it will not re-alert. After an incident resolves,
|
|
68
70
|
`cooldownMinutes` suppresses a fresh "down" alert for that window to prevent
|
|
69
71
|
flapping.
|
|
@@ -119,13 +121,13 @@ have their schema.
|
|
|
119
121
|
Every operation is an owner-scoped action, so the agent can create and triage
|
|
120
122
|
monitors from chat exactly like the UI:
|
|
121
123
|
|
|
122
|
-
| action | method | what it does
|
|
123
|
-
| ------------------- | ------ |
|
|
124
|
-
| `save-monitor` | POST | create or update a monitor (name, url, method, interval, timeout, expected status, assertions, redirects, severity, channels, email recipients, cooldown, enabled). |
|
|
125
|
-
| `list-monitors` | GET | list monitors with latest status, latency, and 24h/7d uptime %.
|
|
126
|
-
| `get-monitor` | GET | one monitor plus its recent check results and incidents.
|
|
127
|
-
| `delete-monitor` | POST | delete a monitor and its results + incidents.
|
|
128
|
-
| `run-monitor-check` | POST | run one check now, record it, and open/resolve incidents.
|
|
124
|
+
| action | method | what it does |
|
|
125
|
+
| ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
126
|
+
| `save-monitor` | POST | create or update a monitor (name, url, method, interval, timeout, expected status, assertions, redirects, severity, channels, email recipients, optional Slack/webhook URLs, cooldown, enabled). |
|
|
127
|
+
| `list-monitors` | GET | list monitors with latest status, latency, and 24h/7d uptime %. |
|
|
128
|
+
| `get-monitor` | GET | one monitor plus its recent check results and incidents. |
|
|
129
|
+
| `delete-monitor` | POST | delete a monitor and its results + incidents. |
|
|
130
|
+
| `run-monitor-check` | POST | run one check now, record it, and open/resolve incidents. |
|
|
129
131
|
|
|
130
132
|
Example prompts:
|
|
131
133
|
|
|
@@ -70,6 +70,10 @@ export const monitors = table("monitors", {
|
|
|
70
70
|
channels: text("channels").notNull().default('["inbox"]'),
|
|
71
71
|
/** Email recipients as JSON array of addresses. */
|
|
72
72
|
emailRecipients: text("email_recipients").notNull().default("[]"),
|
|
73
|
+
/** Optional per-monitor Slack incoming webhook URL (overrides workspace env). */
|
|
74
|
+
slackWebhookUrl: text("slack_webhook_url"),
|
|
75
|
+
/** Optional per-monitor generic webhook URL (overrides workspace env). */
|
|
76
|
+
webhookUrl: text("webhook_url"),
|
|
73
77
|
/** Minutes to suppress repeat alerts while an incident is ongoing. */
|
|
74
78
|
cooldownMinutes: integer("cooldown_minutes").notNull().default(15),
|
|
75
79
|
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
@@ -101,6 +101,8 @@ export interface MonitorInput {
|
|
|
101
101
|
severity?: MonitorSeverity;
|
|
102
102
|
channels?: string[];
|
|
103
103
|
emailRecipients?: string[];
|
|
104
|
+
slackWebhookUrl?: string | null;
|
|
105
|
+
webhookUrl?: string | null;
|
|
104
106
|
cooldownMinutes?: number;
|
|
105
107
|
enabled?: boolean;
|
|
106
108
|
}
|
|
@@ -120,6 +122,8 @@ export interface Monitor {
|
|
|
120
122
|
severity: MonitorSeverity;
|
|
121
123
|
channels: string[];
|
|
122
124
|
emailRecipients: string[];
|
|
125
|
+
slackWebhookUrl: string | null;
|
|
126
|
+
webhookUrl: string | null;
|
|
123
127
|
cooldownMinutes: number;
|
|
124
128
|
enabled: boolean;
|
|
125
129
|
lastStatus: MonitorStatus | null;
|
|
@@ -462,11 +466,50 @@ function normalizeEmailRecipients(recipients: string[] | undefined): string[] {
|
|
|
462
466
|
return normalized;
|
|
463
467
|
}
|
|
464
468
|
|
|
469
|
+
function normalizeOptionalHttpUrl(
|
|
470
|
+
value: string | null | undefined,
|
|
471
|
+
label: string,
|
|
472
|
+
): string | null {
|
|
473
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
474
|
+
if (!trimmed) return null;
|
|
475
|
+
let parsed: URL;
|
|
476
|
+
try {
|
|
477
|
+
parsed = new URL(trimmed);
|
|
478
|
+
} catch {
|
|
479
|
+
throw badRequest(`${label} must be an absolute http(s) URL`);
|
|
480
|
+
}
|
|
481
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
482
|
+
throw badRequest(`${label} must be an absolute http(s) URL`);
|
|
483
|
+
}
|
|
484
|
+
return trimmed;
|
|
485
|
+
}
|
|
486
|
+
|
|
465
487
|
function ensureInboxChannel(channels: string[]): string[] {
|
|
466
488
|
const normalized = channels.map((c) => c.trim()).filter(Boolean);
|
|
467
489
|
return normalized.includes("inbox") ? normalized : ["inbox", ...normalized];
|
|
468
490
|
}
|
|
469
491
|
|
|
492
|
+
function monitorNotifyMetadata(monitor: Monitor): Record<string, unknown> {
|
|
493
|
+
return {
|
|
494
|
+
kind: "uptime_monitor",
|
|
495
|
+
monitorId: monitor.id,
|
|
496
|
+
monitorName: monitor.name,
|
|
497
|
+
url: monitor.url,
|
|
498
|
+
emailRecipients: monitor.emailRecipients,
|
|
499
|
+
requestedChannels: monitor.channels,
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function monitorNotifyDeliveryMetadata(
|
|
504
|
+
monitor: Monitor,
|
|
505
|
+
): Record<string, string> | undefined {
|
|
506
|
+
const delivery: Record<string, string> = {};
|
|
507
|
+
if (monitor.slackWebhookUrl)
|
|
508
|
+
delivery.slackWebhookUrl = monitor.slackWebhookUrl;
|
|
509
|
+
if (monitor.webhookUrl) delivery.webhookUrl = monitor.webhookUrl;
|
|
510
|
+
return Object.keys(delivery).length > 0 ? delivery : undefined;
|
|
511
|
+
}
|
|
512
|
+
|
|
470
513
|
// ---------------------------------------------------------------------------
|
|
471
514
|
// Pure evaluation core (unit-tested)
|
|
472
515
|
// ---------------------------------------------------------------------------
|
|
@@ -891,6 +934,8 @@ function rowToMonitor(row: any): Monitor {
|
|
|
891
934
|
severity: row.severity === "warning" ? "warning" : "critical",
|
|
892
935
|
channels: safeJsonParse<string[]>(row.channels, ["inbox"]),
|
|
893
936
|
emailRecipients: safeJsonParse<string[]>(row.emailRecipients, []),
|
|
937
|
+
slackWebhookUrl: row.slackWebhookUrl?.trim() || null,
|
|
938
|
+
webhookUrl: row.webhookUrl?.trim() || null,
|
|
894
939
|
cooldownMinutes: Number(row.cooldownMinutes ?? 15),
|
|
895
940
|
enabled: row.enabled === true || row.enabled === 1,
|
|
896
941
|
lastStatus: (row.lastStatus ?? null) as MonitorStatus | null,
|
|
@@ -1121,6 +1166,11 @@ export async function saveMonitor(
|
|
|
1121
1166
|
const severity = input.severity === "warning" ? "warning" : "critical";
|
|
1122
1167
|
const emailRecipients = normalizeEmailRecipients(input.emailRecipients);
|
|
1123
1168
|
const channels = normalizeChannels(input.channels, emailRecipients);
|
|
1169
|
+
const slackWebhookUrl = normalizeOptionalHttpUrl(
|
|
1170
|
+
input.slackWebhookUrl,
|
|
1171
|
+
"Slack webhook URL",
|
|
1172
|
+
);
|
|
1173
|
+
const webhookUrl = normalizeOptionalHttpUrl(input.webhookUrl, "Webhook URL");
|
|
1124
1174
|
const cooldownMinutes = clampInt(input.cooldownMinutes ?? 15, 0, 24 * 60, 15);
|
|
1125
1175
|
const enabled = input.enabled ?? true;
|
|
1126
1176
|
|
|
@@ -1138,6 +1188,8 @@ export async function saveMonitor(
|
|
|
1138
1188
|
severity,
|
|
1139
1189
|
channels: JSON.stringify(channels),
|
|
1140
1190
|
emailRecipients: JSON.stringify(emailRecipients),
|
|
1191
|
+
slackWebhookUrl,
|
|
1192
|
+
webhookUrl,
|
|
1141
1193
|
cooldownMinutes,
|
|
1142
1194
|
enabled,
|
|
1143
1195
|
};
|
|
@@ -1440,16 +1492,14 @@ async function notifyMonitorDown(monitor: Monitor, outcome: CheckOutcome) {
|
|
|
1440
1492
|
body: `${host} is ${label}${detail}.${latency}`,
|
|
1441
1493
|
channels: ensureInboxChannel(monitor.channels),
|
|
1442
1494
|
metadata: {
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1495
|
+
...monitorNotifyMetadata(monitor),
|
|
1496
|
+
...(monitorNotifyDeliveryMetadata(monitor)
|
|
1497
|
+
? { delivery: monitorNotifyDeliveryMetadata(monitor) }
|
|
1498
|
+
: {}),
|
|
1447
1499
|
status: outcome.status,
|
|
1448
1500
|
statusCode: outcome.statusCode,
|
|
1449
1501
|
latencyMs: outcome.latencyMs,
|
|
1450
1502
|
failedAssertions: outcome.failedAssertions,
|
|
1451
|
-
emailRecipients: monitor.emailRecipients,
|
|
1452
|
-
requestedChannels: monitor.channels,
|
|
1453
1503
|
},
|
|
1454
1504
|
},
|
|
1455
1505
|
{ owner: monitor.ownerEmail },
|
|
@@ -1475,16 +1525,14 @@ async function notifyMonitorRecovered(
|
|
|
1475
1525
|
body: `${host} is back up${downFor ? ` after ${downFor} of downtime` : ""}.${latency}`,
|
|
1476
1526
|
channels: ensureInboxChannel(monitor.channels),
|
|
1477
1527
|
metadata: {
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1528
|
+
...monitorNotifyMetadata(monitor),
|
|
1529
|
+
...(monitorNotifyDeliveryMetadata(monitor)
|
|
1530
|
+
? { delivery: monitorNotifyDeliveryMetadata(monitor) }
|
|
1531
|
+
: {}),
|
|
1482
1532
|
status: "up",
|
|
1483
1533
|
statusCode: outcome.statusCode,
|
|
1484
1534
|
latencyMs: outcome.latencyMs,
|
|
1485
1535
|
incidentId: incident.id,
|
|
1486
|
-
emailRecipients: monitor.emailRecipients,
|
|
1487
|
-
requestedChannels: monitor.channels,
|
|
1488
1536
|
},
|
|
1489
1537
|
},
|
|
1490
1538
|
{ owner: monitor.ownerEmail },
|
|
@@ -1114,6 +1114,16 @@ const runAnalyticsMigrations = runMigrations(
|
|
|
1114
1114
|
name: "status-pages-scope-updated-idx",
|
|
1115
1115
|
sql: `CREATE INDEX IF NOT EXISTS status_pages_scope_updated_idx ON status_pages (owner_email, org_id, updated_at)`,
|
|
1116
1116
|
},
|
|
1117
|
+
{
|
|
1118
|
+
version: 106,
|
|
1119
|
+
name: "uptime-monitors-slack-webhook-url",
|
|
1120
|
+
sql: `ALTER TABLE monitors ADD COLUMN IF NOT EXISTS slack_webhook_url TEXT`,
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
version: 107,
|
|
1124
|
+
name: "uptime-monitors-webhook-url",
|
|
1125
|
+
sql: `ALTER TABLE monitors ADD COLUMN IF NOT EXISTS webhook_url TEXT`,
|
|
1126
|
+
},
|
|
1117
1127
|
],
|
|
1118
1128
|
{ table: "analytics_migrations" },
|
|
1119
1129
|
);
|