@agent-native/core 0.114.13 → 0.114.16
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/bin/agent-native.js +35 -14
- package/bin/launcher.js +34 -0
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +21 -0
- package/corpus/core/bin/agent-native.js +35 -14
- package/corpus/core/bin/launcher.js +34 -0
- package/corpus/core/docs/content/deployment.mdx +1 -1
- package/corpus/core/docs/content/frames.mdx +1 -1
- package/corpus/core/docs/content/integrations.mdx +27 -27
- package/corpus/core/docs/content/pr-visual-recap.mdx +17 -13
- package/corpus/core/docs/content/workspace-management.mdx +1 -1
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/engine/builder-engine.ts +4 -0
- package/corpus/core/src/client/AgentPanel.tsx +10 -4
- package/corpus/core/src/client/ConnectBuilderCard.tsx +5 -1
- package/corpus/core/src/client/chat/markdown-renderer.tsx +3 -5
- package/corpus/core/src/client/error-format.ts +2 -1
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +2 -18
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +43 -32
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +4 -2
- package/corpus/core/src/client/settings/BackgroundAgentSection.tsx +5 -1
- package/corpus/core/src/client/settings/SettingsPanel.tsx +4 -4
- package/corpus/core/src/client/settings/useBuilderStatus.ts +3 -0
- package/corpus/core/src/localization/default-messages.ts +1 -7
- package/corpus/core/src/server/builder-browser.ts +4 -0
- package/corpus/core/src/server/builder-design-systems.ts +6 -1
- package/corpus/core/src/server/fusion-app.ts +5 -1
- package/corpus/core/src/shared/builder-link-tracking.ts +41 -0
- package/corpus/core/src/shared/index.ts +1 -0
- package/corpus/templates/analytics/AGENTS.md +10 -1
- package/corpus/templates/analytics/actions/save-dashboard-report-subscription.ts +10 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +5 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +20 -32
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +62 -0
- package/corpus/templates/analytics/changelog/2026-07-21-chart-series-controls-stay-open-while-moving-from-the-legend.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-21-dashboard-email-captures-complete-large-dashboards-in-chunks.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +4 -4
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +1 -8
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +39 -7
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +341 -251
- package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +114 -0
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +84 -6
- package/corpus/templates/analytics/server/plugins/db.ts +13 -0
- package/corpus/templates/clips/app/components/library/bulk-action-toolbar.tsx +25 -8
- package/corpus/templates/clips/app/components/library/library-grid.tsx +118 -9
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/components/library/recording-card.tsx +10 -8
- package/corpus/templates/clips/app/components/player/video-player.tsx +10 -4
- package/corpus/templates/clips/app/i18n/en-US.ts +6 -0
- package/corpus/templates/clips/app/routes/_app.trash.tsx +16 -1
- package/corpus/templates/clips/changelog/2026-07-21-choose-which-whisper-model-to-use-in-settings.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-21-fixed-organization-recording-visibility-default.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-21-meeting-notes-stop-when-calls-end.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +90 -78
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +2 -0
- package/corpus/templates/clips/desktop/src/hooks/useWhisperSettings.ts +172 -0
- package/corpus/templates/clips/desktop/src/lib/meeting-call-app.ts +41 -0
- package/corpus/templates/clips/desktop/src/lib/silence-events.ts +1 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +80 -7
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +32 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +268 -20
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_model.rs +285 -164
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +18 -7
- package/corpus/templates/clips/learnings.defaults.md +1 -1
- package/corpus/templates/clips/server/plugins/db.ts +11 -0
- package/corpus/templates/clips/shared/builder-credits.ts +1 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +20 -38
- package/corpus/templates/content/app/components/editor/previewDocumentSaveController.ts +40 -1
- package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +9 -1
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +5 -1
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +9 -1
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +9 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +4 -0
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +10 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -1
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
- package/dist/client/chat/markdown-renderer.js +3 -2
- package/dist/client/chat/markdown-renderer.js.map +1 -1
- package/dist/client/error-format.d.ts +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +1 -1
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +3 -4
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +21 -7
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +2 -2
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.d.ts.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.js +5 -1
- package/dist/client/settings/BackgroundAgentSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +2 -0
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +0 -6
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +1 -7
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +4 -0
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +6 -1
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/fusion-app.d.ts.map +1 -1
- package/dist/server/fusion-app.js +2 -1
- package/dist/server/fusion-app.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/builder-link-tracking.d.ts +14 -0
- package/dist/shared/builder-link-tracking.d.ts.map +1 -0
- package/dist/shared/builder-link-tracking.js +26 -0
- package/dist/shared/builder-link-tracking.js.map +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/docs/content/deployment.mdx +1 -1
- package/docs/content/frames.mdx +1 -1
- package/docs/content/integrations.mdx +27 -27
- package/docs/content/pr-visual-recap.mdx +17 -13
- package/docs/content/workspace-management.mdx +1 -1
- package/package.json +3 -2
- package/src/agent/engine/builder-engine.ts +4 -0
- package/src/client/AgentPanel.tsx +10 -4
- package/src/client/ConnectBuilderCard.tsx +5 -1
- package/src/client/chat/markdown-renderer.tsx +3 -5
- package/src/client/error-format.ts +2 -1
- package/src/client/index.ts +1 -0
- package/src/client/integrations/IntegrationsPanel.tsx +2 -18
- package/src/client/resources/McpIntegrationDialog.tsx +43 -32
- package/src/client/resources/mcp-integration-catalog.ts +4 -2
- package/src/client/settings/BackgroundAgentSection.tsx +5 -1
- package/src/client/settings/SettingsPanel.tsx +4 -4
- package/src/client/settings/useBuilderStatus.ts +3 -0
- package/src/localization/default-messages.ts +1 -7
- package/src/server/builder-browser.ts +4 -0
- package/src/server/builder-design-systems.ts +6 -1
- package/src/server/fusion-app.ts +5 -1
- package/src/shared/builder-link-tracking.ts +41 -0
- package/src/shared/index.ts +1 -0
|
@@ -480,7 +480,7 @@
|
|
|
480
480
|
"chartType": "area",
|
|
481
481
|
"source": "first-party",
|
|
482
482
|
"width": 2,
|
|
483
|
-
"sql": "WITH all_users AS (SELECT NULLIF(user_key, '') AS user_key, event_date, user_id, COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') AS template FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))), first_seen AS (SELECT user_key, MIN(event_date) AS first_date FROM all_users GROUP BY user_key) SELECT a.event_date AS date, a.template AS template, COUNT(DISTINCT a.user_key) AS users FROM all_users a JOIN first_seen f ON f.user_key = a.user_key WHERE a.event_date <> f.first_date AND a.template <> 'unknown' AND (a.event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) GROUP BY 1, 2 ORDER BY date, template",
|
|
483
|
+
"sql": "WITH all_users AS (SELECT NULLIF(user_key, '') AS user_key, event_date, user_id, COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') AS template FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')), first_seen AS (SELECT user_key, MIN(event_date) AS first_date FROM all_users GROUP BY user_key) SELECT a.event_date AS date, a.template AS template, COUNT(DISTINCT a.user_key) AS users FROM all_users a JOIN first_seen f ON f.user_key = a.user_key WHERE a.event_date <> f.first_date AND a.template <> 'unknown' AND (a.event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) GROUP BY 1, 2 ORDER BY date, template",
|
|
484
484
|
"config": {
|
|
485
485
|
"xKey": "date",
|
|
486
486
|
"yKey": "users",
|
|
@@ -491,7 +491,7 @@
|
|
|
491
491
|
"valueKey": "users"
|
|
492
492
|
},
|
|
493
493
|
"stacked": true,
|
|
494
|
-
"description": "Daily signed-in visitors who are
|
|
494
|
+
"description": "Daily signed-in visitors who are not on their first active day observed in the previous 365 days, stacked by inferred template/app used that day. Docs traffic and unknown template are excluded."
|
|
495
495
|
}
|
|
496
496
|
},
|
|
497
497
|
{
|
|
@@ -500,7 +500,7 @@
|
|
|
500
500
|
"chartType": "bar",
|
|
501
501
|
"source": "first-party",
|
|
502
502
|
"width": 1,
|
|
503
|
-
"sql": "WITH all_users AS (SELECT NULLIF(user_key, '') AS user_key, event_date, user_id, COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') AS template FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))), first_seen AS (SELECT user_key, MIN(event_date) AS first_date FROM all_users GROUP BY user_key) SELECT to_char(date_trunc('week', a.event_date::date), 'YYYY-MM-DD') AS date, a.template AS template, COUNT(DISTINCT a.user_key) AS users FROM all_users a JOIN first_seen f ON f.user_key = a.user_key WHERE a.event_date <> f.first_date AND a.template <> 'unknown' AND (a.event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) GROUP BY 1, 2 ORDER BY date, template",
|
|
503
|
+
"sql": "WITH all_users AS (SELECT NULLIF(user_key, '') AS user_key, event_date, user_id, COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown') AS template FROM analytics_events WHERE event_name = 'session status' AND signed_in = 'true' AND NULLIF(user_key, '') IS NOT NULL AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) <> 'docs' AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')), first_seen AS (SELECT user_key, MIN(event_date) AS first_date FROM all_users GROUP BY user_key) SELECT to_char(date_trunc('week', a.event_date::date), 'YYYY-MM-DD') AS date, a.template AS template, COUNT(DISTINCT a.user_key) AS users FROM all_users a JOIN first_seen f ON f.user_key = a.user_key WHERE a.event_date <> f.first_date AND a.template <> 'unknown' AND (a.event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND a.event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) GROUP BY 1, 2 ORDER BY date, template",
|
|
504
504
|
"config": {
|
|
505
505
|
"xKey": "date",
|
|
506
506
|
"yKey": "users",
|
|
@@ -511,7 +511,7 @@
|
|
|
511
511
|
"valueKey": "users"
|
|
512
512
|
},
|
|
513
513
|
"stacked": true,
|
|
514
|
-
"description": "Weekly distinct signed-in visitors who are
|
|
514
|
+
"description": "Weekly distinct signed-in visitors who are not on their first active day observed in the previous 365 days, stacked by inferred template/app used that week. Weeks start Monday; docs traffic and unknown template are excluded."
|
|
515
515
|
}
|
|
516
516
|
},
|
|
517
517
|
{
|
|
@@ -51,7 +51,6 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
51
51
|
() =>
|
|
52
52
|
sendDashboardReportSubscription(sub, {
|
|
53
53
|
skipEmailWithoutScreenshot: retryAt !== null,
|
|
54
|
-
allowLimitedFallback: retryAt === null,
|
|
55
54
|
}),
|
|
56
55
|
);
|
|
57
56
|
if (!result.screenshotAttached) {
|
|
@@ -79,13 +78,7 @@ export async function runDashboardReportsOnce(): Promise<{
|
|
|
79
78
|
await markDashboardReportResult(sub, "error", message);
|
|
80
79
|
continue;
|
|
81
80
|
}
|
|
82
|
-
|
|
83
|
-
const detail = `sent with ${result.screenshotMode} screenshot; earlier attempts failed: ${result.screenshotError}`;
|
|
84
|
-
console.warn(`[dashboard-report] Subscription ${sub.id} ${detail}`);
|
|
85
|
-
await markDashboardReportResult(sub, "success", detail);
|
|
86
|
-
} else {
|
|
87
|
-
await markDashboardReportResult(sub, "success");
|
|
88
|
-
}
|
|
81
|
+
await markDashboardReportResult(sub, "success");
|
|
89
82
|
} catch (err: any) {
|
|
90
83
|
failed++;
|
|
91
84
|
const message = err?.message ?? String(err);
|
|
@@ -6,6 +6,10 @@ import { and, asc, eq, isNull, lte, or, sql } from "drizzle-orm";
|
|
|
6
6
|
import { getDb, schema } from "../db/index.js";
|
|
7
7
|
import { loadDashboardSeed } from "./dashboard-seeds";
|
|
8
8
|
import { getDashboard } from "./dashboards-store";
|
|
9
|
+
import {
|
|
10
|
+
FIRST_PARTY_DASHBOARD_ID,
|
|
11
|
+
repairFirstPartyRecurringUserPanels,
|
|
12
|
+
} from "./first-party-metric-catalog";
|
|
9
13
|
|
|
10
14
|
export interface ReportSubscriptionInput {
|
|
11
15
|
id?: string;
|
|
@@ -43,6 +47,8 @@ export interface AccessCtx {
|
|
|
43
47
|
orgId: string | null;
|
|
44
48
|
}
|
|
45
49
|
|
|
50
|
+
export const MAX_DASHBOARD_REPORT_RECIPIENTS = 5;
|
|
51
|
+
|
|
46
52
|
export interface ReportDashboard {
|
|
47
53
|
id: string;
|
|
48
54
|
title: string;
|
|
@@ -55,10 +61,14 @@ export async function getReportDashboard(
|
|
|
55
61
|
): Promise<ReportDashboard | null> {
|
|
56
62
|
const dashboard = await getDashboard(dashboardId, ctx);
|
|
57
63
|
if (dashboard?.kind === "sql") {
|
|
64
|
+
const config =
|
|
65
|
+
dashboardId === FIRST_PARTY_DASHBOARD_ID
|
|
66
|
+
? repairFirstPartyRecurringUserPanels(dashboard.config).config
|
|
67
|
+
: dashboard.config;
|
|
58
68
|
return {
|
|
59
69
|
id: dashboard.id,
|
|
60
70
|
title: dashboard.title,
|
|
61
|
-
config
|
|
71
|
+
config,
|
|
62
72
|
};
|
|
63
73
|
}
|
|
64
74
|
|
|
@@ -78,6 +88,19 @@ function nowIso(): string {
|
|
|
78
88
|
return new Date().toISOString();
|
|
79
89
|
}
|
|
80
90
|
|
|
91
|
+
const DASHBOARD_REPORT_ERROR_MAX_LENGTH = 2_000;
|
|
92
|
+
const DASHBOARD_REPORT_ERROR_OMISSION = "\n… [truncated] …\n";
|
|
93
|
+
|
|
94
|
+
export function truncateDashboardReportError(error: string): string {
|
|
95
|
+
if (error.length <= DASHBOARD_REPORT_ERROR_MAX_LENGTH) return error;
|
|
96
|
+
|
|
97
|
+
const retainedLength =
|
|
98
|
+
DASHBOARD_REPORT_ERROR_MAX_LENGTH - DASHBOARD_REPORT_ERROR_OMISSION.length;
|
|
99
|
+
const prefixLength = Math.ceil(retainedLength / 2);
|
|
100
|
+
const suffixLength = Math.floor(retainedLength / 2);
|
|
101
|
+
return `${error.slice(0, prefixLength)}${DASHBOARD_REPORT_ERROR_OMISSION}${error.slice(-suffixLength)}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
81
104
|
function safeJsonParse<T>(raw: unknown, fallback: T): T {
|
|
82
105
|
if (typeof raw !== "string") return fallback;
|
|
83
106
|
try {
|
|
@@ -88,7 +111,9 @@ function safeJsonParse<T>(raw: unknown, fallback: T): T {
|
|
|
88
111
|
}
|
|
89
112
|
}
|
|
90
113
|
|
|
91
|
-
function
|
|
114
|
+
export function normalizeDashboardReportRecipients(
|
|
115
|
+
recipients: string[],
|
|
116
|
+
): string[] {
|
|
92
117
|
const seen = new Set<string>();
|
|
93
118
|
const normalized: string[] = [];
|
|
94
119
|
for (const raw of recipients) {
|
|
@@ -97,6 +122,14 @@ function normalizeRecipients(recipients: string[]): string[] {
|
|
|
97
122
|
seen.add(email);
|
|
98
123
|
normalized.push(email);
|
|
99
124
|
}
|
|
125
|
+
if (normalized.length === 0) {
|
|
126
|
+
throw new Error("At least one recipient is required");
|
|
127
|
+
}
|
|
128
|
+
if (normalized.length > MAX_DASHBOARD_REPORT_RECIPIENTS) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Dashboard reports support at most ${MAX_DASHBOARD_REPORT_RECIPIENTS} recipients; use a mailing-list address for larger audiences`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
100
133
|
return normalized;
|
|
101
134
|
}
|
|
102
135
|
|
|
@@ -239,6 +272,8 @@ export function lastDailyRunAt(
|
|
|
239
272
|
}
|
|
240
273
|
|
|
241
274
|
const DASHBOARD_REPORT_RETRY_WINDOW_MS = 60 * 60 * 1000;
|
|
275
|
+
// This is the earliest nextRunAt. The generated */15 cron means the actual
|
|
276
|
+
// retry occurs on the first sweep after this floor, not exactly ten minutes later.
|
|
242
277
|
const DASHBOARD_REPORT_RETRY_DELAY_MS = 10 * 60 * 1000;
|
|
243
278
|
|
|
244
279
|
export function dashboardReportRetryAt(
|
|
@@ -348,10 +383,7 @@ export async function saveDashboardReportSubscription(
|
|
|
348
383
|
throw Object.assign(new Error("Dashboard not found"), { statusCode: 404 });
|
|
349
384
|
}
|
|
350
385
|
|
|
351
|
-
const recipients =
|
|
352
|
-
if (recipients.length === 0) {
|
|
353
|
-
throw new Error("At least one recipient is required");
|
|
354
|
-
}
|
|
386
|
+
const recipients = normalizeDashboardReportRecipients(input.recipients);
|
|
355
387
|
|
|
356
388
|
const timeOfDay = assertTimeOfDay(input.timeOfDay);
|
|
357
389
|
const timezone = assertTimezone(input.timezone);
|
|
@@ -539,7 +571,7 @@ export async function markDashboardReportResult(
|
|
|
539
571
|
.update(schema.dashboardReportSubscriptions)
|
|
540
572
|
.set({
|
|
541
573
|
lastStatus: status,
|
|
542
|
-
lastError: error ? error
|
|
574
|
+
lastError: error ? truncateDashboardReportError(error) : null,
|
|
543
575
|
nextRunAt: sub.enabled
|
|
544
576
|
? (options?.nextRunAt ??
|
|
545
577
|
nextDailyRunAt(sub.timeOfDay, sub.timezone, new Date()))
|