@agent-native/core 0.92.2 → 0.92.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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +32 -0
- package/corpus/core/docs/content/locales/ar-SA/server.mdx +35 -0
- package/corpus/core/docs/content/locales/de-DE/server.mdx +35 -0
- package/corpus/core/docs/content/locales/es-ES/server.mdx +35 -0
- package/corpus/core/docs/content/locales/fr-FR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/hi-IN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ja-JP/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ko-KR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/pt-BR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-CN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-TW/server.mdx +35 -0
- package/corpus/core/docs/content/server.mdx +35 -0
- package/corpus/core/package.json +2 -2
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -4
- package/corpus/core/src/agent/engine/anthropic-engine.ts +15 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +30 -4
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/production-agent.ts +12 -1
- package/corpus/core/src/agent/run-manager.ts +57 -36
- package/corpus/core/src/agent/run-store.ts +81 -2
- package/corpus/core/src/agent/thread-data-builder.ts +33 -2
- package/corpus/core/src/client/AssistantChat.tsx +314 -96
- package/corpus/core/src/client/agent-chat-adapter.ts +17 -1
- package/corpus/core/src/client/chat/repo-helpers.ts +45 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +36 -27
- package/corpus/core/src/client/chat-model-groups.ts +1 -5
- package/corpus/core/src/client/composer/TiptapComposer.tsx +0 -2
- package/corpus/core/src/client/embed-auth.ts +20 -0
- package/corpus/core/src/client/settings/SettingsSection.tsx +66 -5
- package/corpus/core/src/client/sse-event-processor.ts +49 -17
- package/corpus/core/src/client/use-db-sync.ts +31 -9
- package/corpus/core/src/collab/agent-presence.ts +6 -1
- package/corpus/core/src/collab/awareness-store.ts +185 -0
- package/corpus/core/src/collab/awareness.ts +58 -2
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +7 -8
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +7 -18
- package/corpus/core/src/server/core-routes-plugin.ts +21 -2
- package/corpus/core/src/server/embed-session.ts +20 -4
- package/corpus/core/src/styles/agent-native.css +51 -0
- package/corpus/core/src/tracking/providers.ts +33 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +8 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +9 -1
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +41 -11
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +44 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-action-menus-now-close-cleanly-before-opening-hist.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-email-reports-still-send-when-the-screenshot-captu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-alerts-now-suppress-recovery-noise-during-flapping-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-check-history-now-shows-timing-diagnostics-so-slow-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-recovery-alerts-now-send-when-the-original-outage-not.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +29 -16
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +126 -0
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +5 -0
- package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +5 -1
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +305 -31
- package/corpus/templates/analytics/server/plugins/db.ts +15 -0
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -1
- package/corpus/templates/analytics/server/routes/api/uptime-monitors/run.post.ts +57 -0
- package/corpus/templates/clips/actions/list-meetings.ts +12 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-reminders-now-ignore-obvious-solo-personal-calendar-.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-classification.ts +79 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +13 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +14 -3
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +26 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +47 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +52 -33
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +24 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +64 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +28 -2
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +243 -36
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +15 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +43 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +4 -4
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -3
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +0 -2
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +14 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/settings/SettingsSection.d.ts.map +1 -1
- package/dist/client/settings/SettingsSection.js +32 -3
- package/dist/client/settings/SettingsSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +44 -17
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +29 -9
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/agent-presence.d.ts.map +1 -1
- package/dist/collab/agent-presence.js +5 -2
- package/dist/collab/agent-presence.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +1 -1
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +0 -0
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +4 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +46 -2
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +6 -6
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +5 -13
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +5 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +10 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +17 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/styles/agent-native.css +51 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +23 -5
- package/dist/tracking/providers.js.map +1 -1
- package/docs/content/locales/ar-SA/server.mdx +35 -0
- package/docs/content/locales/de-DE/server.mdx +35 -0
- package/docs/content/locales/es-ES/server.mdx +35 -0
- package/docs/content/locales/fr-FR/server.mdx +35 -0
- package/docs/content/locales/hi-IN/server.mdx +35 -0
- package/docs/content/locales/ja-JP/server.mdx +35 -0
- package/docs/content/locales/ko-KR/server.mdx +35 -0
- package/docs/content/locales/pt-BR/server.mdx +35 -0
- package/docs/content/locales/zh-CN/server.mdx +35 -0
- package/docs/content/locales/zh-TW/server.mdx +35 -0
- package/docs/content/server.mdx +35 -0
- package/package.json +2 -2
|
@@ -36,6 +36,7 @@ import { cn } from "@/lib/utils";
|
|
|
36
36
|
|
|
37
37
|
import { fmt, useUptimeT } from "./i18n";
|
|
38
38
|
import type {
|
|
39
|
+
MonitorCheckDiagnostics,
|
|
39
40
|
MonitorDetail as MonitorDetailData,
|
|
40
41
|
MonitorStats,
|
|
41
42
|
MonitorSummary,
|
|
@@ -420,17 +421,22 @@ export function MonitorDetail({
|
|
|
420
421
|
{formatLatency(result.latencyMs)}
|
|
421
422
|
</TableCell>
|
|
422
423
|
<TableCell className="max-w-[220px] text-xs text-muted-foreground">
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
424
|
+
<div className="space-y-1">
|
|
425
|
+
{result.failedAssertions.length > 0 ? (
|
|
426
|
+
<span className="text-amber-500">
|
|
427
|
+
{result.failedAssertions.join("; ")}
|
|
428
|
+
</span>
|
|
429
|
+
) : result.error ? (
|
|
430
|
+
<span className="text-destructive">
|
|
431
|
+
{result.error}
|
|
432
|
+
</span>
|
|
433
|
+
) : (
|
|
434
|
+
<span className="text-emerald-500">{t.ok}</span>
|
|
435
|
+
)}
|
|
436
|
+
<div className="text-[11px] leading-snug text-muted-foreground/80">
|
|
437
|
+
{formatDiagnostics(result.diagnostics)}
|
|
438
|
+
</div>
|
|
439
|
+
</div>
|
|
434
440
|
</TableCell>
|
|
435
441
|
</TableRow>
|
|
436
442
|
);
|
|
@@ -508,6 +514,30 @@ export function MonitorDetail({
|
|
|
508
514
|
);
|
|
509
515
|
}
|
|
510
516
|
|
|
517
|
+
function formatDiagnostics(diagnostics: MonitorCheckDiagnostics): string {
|
|
518
|
+
const timings = diagnostics.timings ?? {};
|
|
519
|
+
const parts = [
|
|
520
|
+
diagnostics.source,
|
|
521
|
+
timings.requestMs != null
|
|
522
|
+
? `headers ${formatLatency(timings.requestMs)}`
|
|
523
|
+
: null,
|
|
524
|
+
timings.ssrfSetupMs != null
|
|
525
|
+
? `ssrf ${formatLatency(timings.ssrfSetupMs)}`
|
|
526
|
+
: null,
|
|
527
|
+
timings.bodyReadMs != null
|
|
528
|
+
? `body ${formatLatency(timings.bodyReadMs)}`
|
|
529
|
+
: null,
|
|
530
|
+
diagnostics.response?.finalHost
|
|
531
|
+
? `host ${diagnostics.response.finalHost}`
|
|
532
|
+
: null,
|
|
533
|
+
diagnostics.error?.kind ? `error ${diagnostics.error.kind}` : null,
|
|
534
|
+
diagnostics.runtime?.commitRef
|
|
535
|
+
? `sha ${diagnostics.runtime.commitRef.slice(0, 7)}`
|
|
536
|
+
: null,
|
|
537
|
+
].filter(Boolean);
|
|
538
|
+
return parts.join(" · ");
|
|
539
|
+
}
|
|
540
|
+
|
|
511
541
|
function StatCard({
|
|
512
542
|
label,
|
|
513
543
|
children,
|
|
@@ -91,6 +91,50 @@ export interface MonitorCheckResult {
|
|
|
91
91
|
latencyMs: number | null;
|
|
92
92
|
error: string | null;
|
|
93
93
|
failedAssertions: string[];
|
|
94
|
+
diagnostics: MonitorCheckDiagnostics;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface MonitorCheckDiagnostics {
|
|
98
|
+
source:
|
|
99
|
+
| "netlify-scheduled"
|
|
100
|
+
| "netlify-runtime"
|
|
101
|
+
| "in-process"
|
|
102
|
+
| "manual"
|
|
103
|
+
| "unknown";
|
|
104
|
+
runtime: {
|
|
105
|
+
nodeEnv?: string;
|
|
106
|
+
netlify?: boolean;
|
|
107
|
+
deployId?: string;
|
|
108
|
+
deployContext?: string;
|
|
109
|
+
commitRef?: string;
|
|
110
|
+
functionName?: string;
|
|
111
|
+
region?: string;
|
|
112
|
+
};
|
|
113
|
+
request: {
|
|
114
|
+
method: MonitorMethod;
|
|
115
|
+
timeoutMs: number;
|
|
116
|
+
followRedirects: boolean;
|
|
117
|
+
assertionTypes: AssertionType[];
|
|
118
|
+
bodyReadRequired: boolean;
|
|
119
|
+
allowPrivateHosts: boolean;
|
|
120
|
+
};
|
|
121
|
+
timings: {
|
|
122
|
+
totalMs?: number;
|
|
123
|
+
ssrfSetupMs?: number;
|
|
124
|
+
requestMs?: number;
|
|
125
|
+
bodyReadMs?: number;
|
|
126
|
+
};
|
|
127
|
+
response?: {
|
|
128
|
+
finalUrl?: string;
|
|
129
|
+
finalHost?: string;
|
|
130
|
+
statusCode?: number;
|
|
131
|
+
headers?: Record<string, string>;
|
|
132
|
+
};
|
|
133
|
+
error?: {
|
|
134
|
+
kind: "config" | "timeout" | "network" | "body-timeout";
|
|
135
|
+
name?: string;
|
|
136
|
+
message: string;
|
|
137
|
+
};
|
|
94
138
|
}
|
|
95
139
|
|
|
96
140
|
export interface MonitorIncident {
|
|
@@ -38,6 +38,10 @@ Each check runs on the server (`server/lib/uptime-monitors.ts`):
|
|
|
38
38
|
- `down` — the status does not match, a content/header assertion fails, or
|
|
39
39
|
the request errored/timed out. Alerts at the monitor's configured severity.
|
|
40
40
|
- `error` — a configuration/SSRF problem prevented the check from running.
|
|
41
|
+
6. Persist a compact diagnostics payload on the check row with safe runtime
|
|
42
|
+
context, phase timings, response headers such as `x-nf-request-id`, and the
|
|
43
|
+
error kind. Diagnostics intentionally exclude request headers, request bodies,
|
|
44
|
+
response bodies, secrets, and large payloads.
|
|
41
45
|
|
|
42
46
|
### Status matcher
|
|
43
47
|
|
|
@@ -73,15 +77,19 @@ Each check runs on the server (`server/lib/uptime-monitors.ts`):
|
|
|
73
77
|
`emailRecipients`; `slack` / `webhook` use the monitor's optional
|
|
74
78
|
delivery-only `slackWebhookUrl` / `webhookUrl` (falling back to workspace
|
|
75
79
|
`NOTIFICATIONS_SLACK_WEBHOOK_URL` / `NOTIFICATIONS_WEBHOOK_URL` when unset).
|
|
76
|
-
- Transient no-response failures, such as network errors or timeouts,
|
|
77
|
-
twice in a row before opening an
|
|
80
|
+
- Transient no-response failures, such as network errors or timeouts, and
|
|
81
|
+
transient latency degradations must fail twice in a row before opening an
|
|
82
|
+
incident or alerting. Set
|
|
78
83
|
`UPTIME_MONITOR_TRANSIENT_FAILURE_CONFIRMATION_CHECKS=1` to alert on the first
|
|
79
84
|
transient failure, or a higher value (max 10) for stricter confirmation.
|
|
80
85
|
- While an incident is open it will not re-alert. After an incident resolves,
|
|
81
86
|
`cooldownMinutes` suppresses a fresh "down" alert for that window to prevent
|
|
82
|
-
flapping.
|
|
87
|
+
flapping. Incidents opened during that quiet window do not send recovery
|
|
88
|
+
emails either, so one flaky target cannot produce "recovered" noise for an
|
|
89
|
+
alert that was intentionally suppressed.
|
|
83
90
|
- On **recovery**, it resolves the open incident and sends an informational
|
|
84
|
-
"recovered" notification (with downtime duration)
|
|
91
|
+
"recovered" notification (with downtime duration) only when the incident sent
|
|
92
|
+
an initial down/degraded notification.
|
|
85
93
|
|
|
86
94
|
A `lastStatus === "running"` claim guard (with a staleness timeout) ensures that
|
|
87
95
|
concurrent sweeps never double-run the same monitor.
|
|
@@ -105,23 +113,28 @@ set `UPTIME_MONITOR_ALLOW_PRIVATE_HOSTS=1` (off by default).
|
|
|
105
113
|
|
|
106
114
|
## Background job
|
|
107
115
|
|
|
108
|
-
The sweep is registered by `server/plugins/uptime-monitor-jobs.ts`
|
|
116
|
+
The sweep is registered by `server/plugins/uptime-monitor-jobs.ts` for
|
|
117
|
+
long-lived runtimes. Netlify builds emit a scheduled trigger plus background
|
|
118
|
+
worker from `scripts/emit-netlify-dashboard-report-cron.ts`; the route
|
|
119
|
+
`/api/uptime-monitors/run` is callable by that generated worker or by an
|
|
120
|
+
external cron with `UPTIME_MONITORS_CRON_SECRET`. Both paths run
|
|
109
121
|
`runDueMonitorsOnce()` (`server/jobs/uptime-monitors.ts`), which selects enabled
|
|
110
122
|
monitors whose interval has elapsed, checks them, records results, opens/resolves
|
|
111
123
|
incidents, and prunes old check results.
|
|
112
124
|
|
|
113
125
|
Environment flags (mirrors the analytics alert job):
|
|
114
126
|
|
|
115
|
-
| flag | effect
|
|
116
|
-
| ------------------------------------------------------ |
|
|
117
|
-
| `UPTIME_MONITOR_JOBS` | `1` enables the in-process cron; `0` disables it.
|
|
118
|
-
| `RUN_BACKGROUND_JOBS` | fallback flag when `UPTIME_MONITOR_JOBS` is unset.
|
|
119
|
-
| (production) | on by default unless a flag is `0` or a platform scheduler owns it.
|
|
120
|
-
| `UPTIME_MONITOR_INTERVAL_MS` | wake interval for the sweep (default 30s, min 10s).
|
|
121
|
-
| `UPTIME_MONITOR_SWEEP_LIMIT` | max monitors processed per sweep (default 100).
|
|
122
|
-
| `UPTIME_MONITOR_RESULT_RETENTION_DAYS` | how long check results are kept (default 30).
|
|
123
|
-
| `UPTIME_MONITOR_ALLOW_PRIVATE_HOSTS` | allow probing private/internal hosts.
|
|
124
|
-
| `UPTIME_MONITOR_TRANSIENT_FAILURE_CONFIRMATION_CHECKS` | consecutive timeout/network failures required before alerting (default 2, max 10). |
|
|
127
|
+
| flag | effect |
|
|
128
|
+
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
|
|
129
|
+
| `UPTIME_MONITOR_JOBS` | `1` enables the in-process cron; `0` disables it. |
|
|
130
|
+
| `RUN_BACKGROUND_JOBS` | fallback flag when `UPTIME_MONITOR_JOBS` is unset. |
|
|
131
|
+
| (production) | on by default unless a flag is `0` or a platform scheduler owns it. |
|
|
132
|
+
| `UPTIME_MONITOR_INTERVAL_MS` | wake interval for the sweep (default 30s, min 10s). |
|
|
133
|
+
| `UPTIME_MONITOR_SWEEP_LIMIT` | max monitors processed per sweep (default 100). |
|
|
134
|
+
| `UPTIME_MONITOR_RESULT_RETENTION_DAYS` | how long check results are kept (default 30). |
|
|
135
|
+
| `UPTIME_MONITOR_ALLOW_PRIVATE_HOSTS` | allow probing private/internal hosts. |
|
|
136
|
+
| `UPTIME_MONITOR_TRANSIENT_FAILURE_CONFIRMATION_CHECKS` | consecutive timeout/network/slow-response failures required before alerting (default 2, max 10). |
|
|
137
|
+
| `UPTIME_MONITORS_CRON_SECRET` | bearer token for external cron callers of `/api/uptime-monitors/run`. |
|
|
125
138
|
|
|
126
139
|
The monitor tables (`monitors`, `monitor_check_results`, `monitor_incidents`)
|
|
127
140
|
are created by the app migration list in `server/plugins/db.ts`, which runs at
|
|
@@ -157,7 +170,7 @@ All three tables carry `ownableColumns()` (`owner_email` / `org_id` /
|
|
|
157
170
|
`last_checked_at`, `last_success_at`, `last_error`, `last_latency_ms`,
|
|
158
171
|
`last_status_code`, `consecutive_failures`).
|
|
159
172
|
- **`monitor_check_results`** — one row per probe (`ok`, `status`, `status_code`,
|
|
160
|
-
`latency_ms`, `error`, `failed_assertions`), indexed on
|
|
173
|
+
`latency_ms`, `error`, `failed_assertions`, `diagnostics`), indexed on
|
|
161
174
|
`(monitor_id, checked_at)`; pruned by the sweep so it stays bounded.
|
|
162
175
|
- **`monitor_incidents`** — one row per failure streak (`started_at`,
|
|
163
176
|
`resolved_at` null = ongoing, `cause`, `severity`, `notification_id`,
|
|
@@ -16,6 +16,10 @@ const ALERT_SCHEDULED_NAME = "analytics-alert-cron";
|
|
|
16
16
|
const ALERT_WORKER_NAME = "analytics-alert-sweep-background";
|
|
17
17
|
const ALERT_ROUTE_PATH = "/api/analytics-alerts/run";
|
|
18
18
|
const ALERT_SCHEDULE = "*/5 * * * *";
|
|
19
|
+
const UPTIME_SCHEDULED_NAME = "uptime-monitor-cron";
|
|
20
|
+
const UPTIME_WORKER_NAME = "uptime-monitor-sweep-background";
|
|
21
|
+
const UPTIME_ROUTE_PATH = "/api/uptime-monitors/run";
|
|
22
|
+
const UPTIME_SCHEDULE = "* * * * *";
|
|
19
23
|
|
|
20
24
|
function ensureDir(dir: string) {
|
|
21
25
|
mkdirSync(dir, { recursive: true });
|
|
@@ -236,6 +240,122 @@ export const config = {
|
|
|
236
240
|
writeFileSync(path.join(dest, `${ALERT_WORKER_NAME}.mjs`), source);
|
|
237
241
|
}
|
|
238
242
|
|
|
243
|
+
function emitUptimeScheduledTrigger(token: string) {
|
|
244
|
+
const dest = path.join(FUNCTIONS_DIR, UPTIME_SCHEDULED_NAME);
|
|
245
|
+
rmSync(dest, { recursive: true, force: true });
|
|
246
|
+
ensureDir(dest);
|
|
247
|
+
|
|
248
|
+
const source = `const WORKER_PATH = "/.netlify/functions/${UPTIME_WORKER_NAME}";
|
|
249
|
+
const CRON_TOKEN = ${JSON.stringify(token)};
|
|
250
|
+
|
|
251
|
+
function siteOrigin(request) {
|
|
252
|
+
const configured = process.env.URL || process.env.DEPLOY_URL;
|
|
253
|
+
if (configured) return configured;
|
|
254
|
+
const url = new URL(request.url);
|
|
255
|
+
return url.origin;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function readScheduledInvocation(request) {
|
|
259
|
+
if (request.method !== "POST") return null;
|
|
260
|
+
let body;
|
|
261
|
+
try {
|
|
262
|
+
body = await request.json();
|
|
263
|
+
} catch {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
const nextRun = typeof body?.next_run === "string" ? body.next_run : "";
|
|
267
|
+
return nextRun && Number.isFinite(Date.parse(nextRun))
|
|
268
|
+
? { nextRun }
|
|
269
|
+
: null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export default async function handler(request) {
|
|
273
|
+
const scheduled = await readScheduledInvocation(request);
|
|
274
|
+
if (!scheduled) return new Response("Not Found", { status: 404 });
|
|
275
|
+
|
|
276
|
+
const url = new URL(WORKER_PATH, siteOrigin(request));
|
|
277
|
+
const response = await fetch(url.toString(), {
|
|
278
|
+
method: "POST",
|
|
279
|
+
headers: {
|
|
280
|
+
"content-type": "application/json",
|
|
281
|
+
"x-agent-native-uptime-monitor-cron": CRON_TOKEN,
|
|
282
|
+
},
|
|
283
|
+
body: JSON.stringify({ scheduled: true, next_run: scheduled.nextRun }),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
if (!response.ok && response.status !== 202) {
|
|
287
|
+
console.error(
|
|
288
|
+
"[uptime-monitor-cron] Background sweep trigger failed:",
|
|
289
|
+
response.status,
|
|
290
|
+
await response.text().catch(() => ""),
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export const config = {
|
|
296
|
+
name: "uptime monitor cron trigger",
|
|
297
|
+
generator: "agent-native analytics build",
|
|
298
|
+
schedule: ${JSON.stringify(UPTIME_SCHEDULE)},
|
|
299
|
+
};
|
|
300
|
+
`;
|
|
301
|
+
|
|
302
|
+
writeFileSync(path.join(dest, `${UPTIME_SCHEDULED_NAME}.mjs`), source);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function emitUptimeBackgroundWorker(token: string) {
|
|
306
|
+
const dest = path.join(FUNCTIONS_DIR, UPTIME_WORKER_NAME);
|
|
307
|
+
rmSync(dest, { recursive: true, force: true });
|
|
308
|
+
cpSync(SERVER_DIR, dest, { recursive: true });
|
|
309
|
+
rmSync(path.join(dest, "server.mjs"), { force: true });
|
|
310
|
+
|
|
311
|
+
const source = `globalThis.__AGENT_NATIVE_UPTIME_MONITOR_SCHEDULED_RUNTIME__ = true;
|
|
312
|
+
|
|
313
|
+
const CRON_TOKEN = ${JSON.stringify(token)};
|
|
314
|
+
const ROUTE_PATH = ${JSON.stringify(UPTIME_ROUTE_PATH)};
|
|
315
|
+
let cachedHandler;
|
|
316
|
+
|
|
317
|
+
function timingSafeEquals(a, b) {
|
|
318
|
+
if (a.length !== b.length) return false;
|
|
319
|
+
let diff = 0;
|
|
320
|
+
for (let i = 0; i < a.length; i += 1) {
|
|
321
|
+
diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
|
|
322
|
+
}
|
|
323
|
+
return diff === 0;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export default async function handler(request, context) {
|
|
327
|
+
const token = request.headers.get("x-agent-native-uptime-monitor-cron") || "";
|
|
328
|
+
if (!timingSafeEquals(token, CRON_TOKEN)) {
|
|
329
|
+
return new Response("Unauthorized", { status: 401 });
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
cachedHandler ??= (await import("./main.mjs")).default;
|
|
333
|
+
const url = new URL(request.url);
|
|
334
|
+
url.pathname = ROUTE_PATH;
|
|
335
|
+
url.search = "";
|
|
336
|
+
|
|
337
|
+
const rewritten = new Request(url.toString(), {
|
|
338
|
+
method: "POST",
|
|
339
|
+
headers: new Headers({ "content-type": "application/json" }),
|
|
340
|
+
body: JSON.stringify({ scheduled: true }),
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
return await cachedHandler(rewritten, context);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export const config = {
|
|
347
|
+
name: "uptime monitor background sweep",
|
|
348
|
+
generator: "agent-native analytics build",
|
|
349
|
+
background: true,
|
|
350
|
+
nodeBundler: "none",
|
|
351
|
+
includedFiles: ["**"],
|
|
352
|
+
preferStatic: false,
|
|
353
|
+
};
|
|
354
|
+
`;
|
|
355
|
+
|
|
356
|
+
writeFileSync(path.join(dest, `${UPTIME_WORKER_NAME}.mjs`), source);
|
|
357
|
+
}
|
|
358
|
+
|
|
239
359
|
function isDirectRun(): boolean {
|
|
240
360
|
const entrypoint = process.argv[1];
|
|
241
361
|
return Boolean(
|
|
@@ -254,16 +374,22 @@ function main(): void {
|
|
|
254
374
|
|
|
255
375
|
const token = randomBytes(32).toString("hex");
|
|
256
376
|
const alertToken = randomBytes(32).toString("hex");
|
|
377
|
+
const uptimeToken = randomBytes(32).toString("hex");
|
|
257
378
|
emitScheduledTrigger(token);
|
|
258
379
|
emitBackgroundWorker(token);
|
|
259
380
|
emitAlertScheduledTrigger(alertToken);
|
|
260
381
|
emitAlertBackgroundWorker(alertToken);
|
|
382
|
+
emitUptimeScheduledTrigger(uptimeToken);
|
|
383
|
+
emitUptimeBackgroundWorker(uptimeToken);
|
|
261
384
|
console.log(
|
|
262
385
|
`[dashboard-report-cron] Emitted Netlify scheduled trigger "${SCHEDULED_NAME}" (${SCHEDULE}) and background worker "${WORKER_NAME}".`,
|
|
263
386
|
);
|
|
264
387
|
console.log(
|
|
265
388
|
`[analytics-alert-cron] Emitted Netlify scheduled trigger "${ALERT_SCHEDULED_NAME}" (${ALERT_SCHEDULE}) and background worker "${ALERT_WORKER_NAME}".`,
|
|
266
389
|
);
|
|
390
|
+
console.log(
|
|
391
|
+
`[uptime-monitor-cron] Emitted Netlify scheduled trigger "${UPTIME_SCHEDULED_NAME}" (${UPTIME_SCHEDULE}) and background worker "${UPTIME_WORKER_NAME}".`,
|
|
392
|
+
);
|
|
267
393
|
}
|
|
268
394
|
|
|
269
395
|
if (isDirectRun()) {
|
|
@@ -109,6 +109,8 @@ export const monitorCheckResults = table("monitor_check_results", {
|
|
|
109
109
|
error: text("error"),
|
|
110
110
|
/** JSON array of human-readable assertion failures. */
|
|
111
111
|
failedAssertions: text("failed_assertions").notNull().default("[]"),
|
|
112
|
+
/** Compact JSON with phase timings and safe runtime/response metadata. */
|
|
113
|
+
diagnostics: text("diagnostics").notNull().default("{}"),
|
|
112
114
|
createdAt: text("created_at").notNull().default(now()),
|
|
113
115
|
...ownableColumns(),
|
|
114
116
|
});
|
|
@@ -129,6 +131,9 @@ export const monitorIncidents = table("monitor_incidents", {
|
|
|
129
131
|
cause: text("cause").notNull().default(""),
|
|
130
132
|
lastError: text("last_error"),
|
|
131
133
|
notificationId: text("notification_id"),
|
|
134
|
+
notificationDelivered: integer("notification_delivered", { mode: "boolean" })
|
|
135
|
+
.notNull()
|
|
136
|
+
.default(false),
|
|
132
137
|
checksFailed: integer("checks_failed").notNull().default(1),
|
|
133
138
|
createdAt: text("created_at").notNull().default(now()),
|
|
134
139
|
...ownableColumns(),
|
|
@@ -51,6 +51,7 @@ export async function runDueMonitorsOnce(
|
|
|
51
51
|
ownerEmail?: string;
|
|
52
52
|
orgId?: string | null;
|
|
53
53
|
limit?: number;
|
|
54
|
+
source?: "netlify-scheduled" | "in-process";
|
|
54
55
|
} = {},
|
|
55
56
|
): Promise<UptimeSweepResult> {
|
|
56
57
|
if (running) {
|
|
@@ -94,7 +95,10 @@ export async function runDueMonitorsOnce(
|
|
|
94
95
|
const claimed = await claimMonitorRun(monitor);
|
|
95
96
|
if (!claimed) continue;
|
|
96
97
|
processed++;
|
|
97
|
-
const outcome = await runMonitorCheck(monitor, {
|
|
98
|
+
const outcome = await runMonitorCheck(monitor, {
|
|
99
|
+
allowPrivateHosts,
|
|
100
|
+
source: options.source,
|
|
101
|
+
});
|
|
98
102
|
await recordMonitorResult(monitor, outcome);
|
|
99
103
|
await evaluateAndNotifyMonitor(monitor, outcome, {
|
|
100
104
|
email: monitor.ownerEmail,
|