@agent-native/core 0.134.1 → 0.135.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 +3 -3
- package/corpus/core/CHANGELOG.md +107 -0
- package/corpus/core/package.json +3 -2
- package/corpus/core/src/automations/service.ts +41 -4
- package/corpus/core/src/cli/mcp-config-writers.ts +27 -21
- package/corpus/core/src/client/agent-page/AgentJobsTab.tsx +187 -85
- package/corpus/core/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
- package/corpus/core/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
- package/corpus/core/src/client/agent-page/TimezoneSelect.tsx +241 -0
- package/corpus/core/src/client/agent-page/use-jobs.ts +50 -10
- package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -0
- package/corpus/core/src/client/settings/AccountSettingsCard.tsx +35 -31
- package/corpus/core/src/client/settings/SchedulingTimezoneField.tsx +71 -0
- package/corpus/core/src/client/settings/SettingsRow.tsx +113 -0
- package/corpus/core/src/client/settings/agent-settings-search.ts +2 -1
- package/corpus/core/src/client/settings/index.ts +6 -0
- package/corpus/core/src/integrations/identity.ts +2 -12
- package/corpus/core/src/jobs/actions/list-automation-runs.ts +33 -0
- package/corpus/core/src/jobs/actions/list-recurring-jobs.ts +27 -6
- package/corpus/core/src/jobs/actions/manage-recurring-job.ts +41 -9
- package/corpus/core/src/jobs/background-automation-runner.ts +88 -0
- package/corpus/core/src/jobs/cron.ts +52 -6
- package/corpus/core/src/jobs/frontmatter.ts +20 -0
- package/corpus/core/src/jobs/run-history.ts +242 -0
- package/corpus/core/src/jobs/scheduler.ts +73 -18
- package/corpus/core/src/jobs/tools.ts +54 -6
- package/corpus/core/src/localization/actions/get-localization-preference.ts +2 -2
- package/corpus/core/src/localization/actions/set-localization-preference.ts +49 -11
- package/corpus/core/src/localization/default-messages.ts +27 -0
- package/corpus/core/src/localization/shared.ts +39 -6
- package/corpus/core/src/localization/user-timezone.ts +36 -0
- package/corpus/core/src/org/app-roles-handlers.ts +1 -9
- package/corpus/core/src/org/index.ts +2 -0
- package/corpus/core/src/org/membership.ts +18 -0
- package/corpus/core/src/review/actions/create-review-comment.ts +4 -1
- package/corpus/core/src/review/actions/reply-review-comment.ts +4 -1
- package/corpus/core/src/review/index.ts +5 -0
- package/corpus/core/src/review/notifications.ts +163 -0
- package/corpus/core/src/review/types.ts +7 -0
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/activity-notifications.ts +174 -0
- package/corpus/core/src/server/app-name.ts +31 -0
- package/corpus/core/src/server/better-auth-instance.ts +4 -4
- package/corpus/core/src/server/email-templates.ts +70 -14
- package/corpus/core/src/server/email.ts +66 -3
- package/corpus/core/src/server/index.ts +10 -0
- package/corpus/core/src/sharing/index.ts +5 -0
- package/corpus/core/src/sharing/recipients.ts +67 -0
- package/corpus/core/src/triggers/actions/list-automations.ts +27 -8
- package/corpus/core/src/triggers/actions/manage-automation.ts +17 -6
- package/corpus/core/src/triggers/actions.ts +16 -4
- package/corpus/core/src/triggers/dispatcher.ts +44 -30
- package/corpus/core/src/triggers/routes.ts +19 -6
- package/corpus/core/src/vitest-config.ts +68 -0
- package/corpus/core/vitest.config.ts +5 -0
- package/corpus/templates/analytics/app/pages/Settings.tsx +37 -53
- package/corpus/templates/analytics/changelog/2026-08-01-new-error-alerts-now-reach-you-by-email-and-a-scheduled-dash.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +43 -0
- package/corpus/templates/analytics/server/lib/error-capture.ts +5 -1
- package/corpus/templates/analytics/vitest.config.ts +16 -12
- package/corpus/templates/assets/actions/refresh-generation-run.ts +3 -0
- package/corpus/templates/assets/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/assets/app/hooks/use-assets-prefs.ts +55 -0
- package/corpus/templates/assets/app/i18n-data.ts +30 -0
- package/corpus/templates/assets/app/routes/settings.tsx +58 -55
- package/corpus/templates/assets/changelog/2026-08-01-get-an-email-when-a-generation-you-started-finishes-or-fails.md +6 -0
- package/corpus/templates/assets/server/lib/generation-run-notifications.ts +89 -0
- package/corpus/templates/assets/server/lib/video-runs.ts +8 -1
- package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.get.ts +17 -0
- package/corpus/templates/assets/server/routes/_agent-native/assets/user-prefs.put.ts +36 -0
- package/corpus/templates/assets/shared/assets-user-prefs.ts +10 -0
- package/corpus/templates/assets/vitest.config.ts +16 -12
- package/corpus/templates/brain/app/routes/settings.tsx +15 -15
- package/corpus/templates/brain/vitest.config.ts +16 -12
- package/corpus/templates/calendar/app/pages/Settings.tsx +32 -40
- package/corpus/templates/calendar/changelog/2026-08-01-language-and-appearance-now-sit-together-in-one-preferences-.md +6 -0
- package/corpus/templates/calendar/vitest.config.ts +16 -12
- package/corpus/templates/chat/app/routes/settings.tsx +14 -22
- package/corpus/templates/chat/vitest.config.ts +9 -0
- package/corpus/templates/clips/.agents/skills/video-editing/SKILL.md +31 -0
- package/corpus/templates/clips/AGENTS.md +1 -0
- package/corpus/templates/clips/actions/add-comment.ts +12 -1
- package/corpus/templates/clips/actions/create-meeting.ts +2 -2
- package/corpus/templates/clips/actions/create-recording.ts +2 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +42 -6
- package/corpus/templates/clips/actions/generate-filmstrip.ts +154 -0
- package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -0
- package/corpus/templates/clips/actions/import-loom-recording.ts +2 -2
- package/corpus/templates/clips/actions/lib/ensure-recording-filmstrip.ts +258 -0
- package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +11 -3
- package/corpus/templates/clips/actions/react-to-comment.ts +13 -0
- package/corpus/templates/clips/actions/react-to-recording.ts +10 -1
- package/corpus/templates/clips/actions/reply-to-comment.ts +12 -1
- package/corpus/templates/clips/actions/stitch-recordings.ts +2 -2
- package/corpus/templates/clips/app/components/editor/editor-layout.tsx +152 -23
- package/corpus/templates/clips/app/components/editor/waveform.tsx +237 -75
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +23 -1
- package/corpus/templates/clips/app/components/settings/ai-setup-section.tsx +441 -0
- package/corpus/templates/clips/app/components/settings/slack-section.tsx +329 -0
- package/corpus/templates/clips/app/components/settings/types.ts +14 -0
- package/corpus/templates/clips/app/components/settings/video-storage-section.tsx +443 -0
- package/corpus/templates/clips/app/hooks/use-secret-status.ts +69 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +9 -1
- package/corpus/templates/clips/app/lib/video-filmstrip.ts +262 -0
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +165 -1351
- package/corpus/templates/clips/changelog/2026-07-27-saved-clips-now-resume-from-the-last-uploaded-byte-after-an-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-editor-timeline-now-shows-video-frames-behind-the-waveform-g.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-email-notifications-now-go-out-when-someone-comments-on-or-r.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-31-you-can-now-set-a-default-visibility-for-new-recordings-in-s.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-01-email-notifications-for-comments-and-reactions-now-actually-.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-03-the-desktop-app-now-opens-directly-beneath-its-tray-icon-on-.md +6 -0
- package/corpus/templates/clips/chrome-extension/package.json +1 -0
- package/corpus/templates/clips/chrome-extension/vitest.config.ts +9 -0
- package/corpus/templates/clips/desktop/package.json +1 -0
- package/corpus/templates/clips/desktop/src/app.tsx +17 -1
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +331 -36
- package/corpus/templates/clips/desktop/src/lib/upload-recovery.ts +129 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +815 -15
- package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +61 -11
- package/corpus/templates/clips/desktop/vitest.config.ts +9 -0
- package/corpus/templates/clips/server/db/schema.ts +17 -0
- package/corpus/templates/clips/server/jobs/media-verification.ts +4 -0
- package/corpus/templates/clips/server/lib/activity-notifications.ts +185 -0
- package/corpus/templates/clips/server/lib/recording-media-cleanup.ts +2 -0
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +46 -9
- package/corpus/templates/clips/server/lib/recordings.ts +24 -0
- package/corpus/templates/clips/server/lib/resumable-session.ts +10 -4
- package/corpus/templates/clips/server/lib/transactional-email-templates.ts +109 -0
- package/corpus/templates/clips/server/lib/upload-interruption.ts +8 -0
- package/corpus/templates/clips/server/lib/upload-lease.ts +50 -4
- package/corpus/templates/clips/server/lib/video-filmstrip-sprite.ts +207 -0
- package/corpus/templates/clips/server/lib/video-remux.ts +13 -4
- package/corpus/templates/clips/server/plugins/db.ts +16 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +4 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +123 -36
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +159 -29
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +197 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +193 -39
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/resume.get.ts +191 -12
- package/corpus/templates/clips/shared/clips-ai-prefs.ts +6 -0
- package/corpus/templates/clips/shared/feature-flags.ts +12 -0
- package/corpus/templates/clips/shared/recording-core.ts +4 -0
- package/corpus/templates/clips/vitest.config.ts +23 -19
- package/corpus/templates/content/actions/add-comment.ts +15 -1
- package/corpus/templates/content/actions/list-documents.ts +1 -0
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +5 -2
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +50 -1
- package/corpus/templates/content/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/content/app/hooks/use-content-prefs.ts +55 -0
- package/corpus/templates/content/app/i18n-data.ts +40 -0
- package/corpus/templates/content/app/routes/_app.settings.tsx +47 -18
- package/corpus/templates/content/changelog/2026-07-30-breadcrumb-menus-now-hide-internal-system-pages.md +6 -0
- package/corpus/templates/content/changelog/2026-08-01-get-an-email-when-someone-comments-on-replies-in-or-mentions.md +6 -0
- package/corpus/templates/content/server/lib/comment-notifications.ts +146 -0
- package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.get.ts +17 -0
- package/corpus/templates/content/server/routes/_agent-native/content/user-prefs.put.ts +36 -0
- package/corpus/templates/content/shared/content-user-prefs.ts +11 -0
- package/corpus/templates/content/vitest.config.ts +20 -16
- package/corpus/templates/crm/app/routes/settings.tsx +14 -22
- package/corpus/templates/crm/vitest.config.ts +8 -4
- package/corpus/templates/design/app/routes/settings.tsx +14 -22
- package/corpus/templates/design/changelog/2026-08-01-review-comments-replies-and-mentions-now-send-an-email-to-th.md +6 -0
- package/corpus/templates/design/vitest.config.ts +18 -12
- package/corpus/templates/dispatch/app/routes/settings.tsx +26 -40
- package/corpus/templates/dispatch/vitest.config.ts +11 -7
- package/corpus/templates/factory/app/routes/settings.tsx +14 -22
- package/corpus/templates/factory/vitest.config.ts +9 -0
- package/corpus/templates/forms/app/routes/_app.settings.tsx +14 -22
- package/corpus/templates/forms/vitest.config.ts +27 -23
- package/corpus/templates/macros/app/routes/settings.tsx +14 -22
- package/corpus/templates/macros/vitest.config.ts +16 -12
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +14 -16
- package/corpus/templates/mail/vitest.config.ts +16 -12
- package/corpus/templates/plan/app/routes/settings.tsx +30 -44
- package/corpus/templates/plan/vitest.config.ts +24 -20
- package/corpus/templates/slides/actions/add-slide-comment.ts +12 -1
- package/corpus/templates/slides/app/components/ui/switch.tsx +1 -0
- package/corpus/templates/slides/app/hooks/use-slides-prefs.ts +55 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
- package/corpus/templates/slides/app/routes/settings.tsx +45 -22
- package/corpus/templates/slides/changelog/2026-08-01-get-an-email-when-someone-comments-on-or-replies-in-your-dec.md +6 -0
- package/corpus/templates/slides/server/lib/comment-notifications.ts +182 -0
- package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.get.ts +16 -0
- package/corpus/templates/slides/server/routes/_agent-native/slides/user-prefs.put.ts +36 -0
- package/corpus/templates/slides/shared/slides-user-prefs.ts +11 -0
- package/corpus/templates/slides/vitest.config.ts +17 -13
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +2 -0
- package/corpus/templates/tasks/app/routes/settings.tsx +14 -22
- package/corpus/templates/tasks/vitest.config.ts +17 -13
- package/corpus/toolkit/CHANGELOG.md +9 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/vitest.config.ts +3 -0
- package/dist/automations/service.d.ts +2 -0
- package/dist/automations/service.d.ts.map +1 -1
- package/dist/automations/service.js +29 -4
- package/dist/automations/service.js.map +1 -1
- package/dist/cli/mcp-config-writers.d.ts.map +1 -1
- package/dist/cli/mcp-config-writers.js +24 -23
- package/dist/cli/mcp-config-writers.js.map +1 -1
- package/dist/client/agent-page/AgentJobsTab.d.ts.map +1 -1
- package/dist/client/agent-page/AgentJobsTab.js +86 -29
- package/dist/client/agent-page/AgentJobsTab.js.map +1 -1
- package/dist/client/agent-page/AutomationDetailsDialog.d.ts +21 -0
- package/dist/client/agent-page/AutomationDetailsDialog.d.ts.map +1 -0
- package/dist/client/agent-page/AutomationDetailsDialog.js +33 -0
- package/dist/client/agent-page/AutomationDetailsDialog.js.map +1 -0
- package/dist/client/agent-page/AutomationScheduleDialog.d.ts +15 -0
- package/dist/client/agent-page/AutomationScheduleDialog.d.ts.map +1 -0
- package/dist/client/agent-page/AutomationScheduleDialog.js +46 -0
- package/dist/client/agent-page/AutomationScheduleDialog.js.map +1 -0
- package/dist/client/agent-page/TimezoneSelect.d.ts +17 -0
- package/dist/client/agent-page/TimezoneSelect.d.ts.map +1 -0
- package/dist/client/agent-page/TimezoneSelect.js +107 -0
- package/dist/client/agent-page/TimezoneSelect.js.map +1 -0
- package/dist/client/agent-page/use-jobs.d.ts +20 -7
- package/dist/client/agent-page/use-jobs.d.ts.map +1 -1
- package/dist/client/agent-page/use-jobs.js +24 -4
- package/dist/client/agent-page/use-jobs.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +8 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +8 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/AccountSettingsCard.d.ts.map +1 -1
- package/dist/client/settings/AccountSettingsCard.js +10 -9
- package/dist/client/settings/AccountSettingsCard.js.map +1 -1
- package/dist/client/settings/SchedulingTimezoneField.d.ts +10 -0
- package/dist/client/settings/SchedulingTimezoneField.d.ts.map +1 -0
- package/dist/client/settings/SchedulingTimezoneField.js +30 -0
- package/dist/client/settings/SchedulingTimezoneField.js.map +1 -0
- package/dist/client/settings/SettingsRow.d.ts +36 -0
- package/dist/client/settings/SettingsRow.d.ts.map +1 -0
- package/dist/client/settings/SettingsRow.js +15 -0
- package/dist/client/settings/SettingsRow.js.map +1 -0
- package/dist/client/settings/agent-settings-search.d.ts.map +1 -1
- package/dist/client/settings/agent-settings-search.js +1 -1
- package/dist/client/settings/agent-settings-search.js.map +1 -1
- package/dist/client/settings/index.d.ts +1 -0
- package/dist/client/settings/index.d.ts.map +1 -1
- package/dist/client/settings/index.js +1 -0
- package/dist/client/settings/index.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/integrations/identity.d.ts.map +1 -1
- package/dist/integrations/identity.js +2 -11
- package/dist/integrations/identity.js.map +1 -1
- package/dist/jobs/actions/list-automation-runs.d.ts +8 -0
- package/dist/jobs/actions/list-automation-runs.d.ts.map +1 -0
- package/dist/jobs/actions/list-automation-runs.js +29 -0
- package/dist/jobs/actions/list-automation-runs.js.map +1 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts +2 -0
- package/dist/jobs/actions/list-recurring-jobs.d.ts.map +1 -1
- package/dist/jobs/actions/list-recurring-jobs.js +20 -7
- package/dist/jobs/actions/list-recurring-jobs.js.map +1 -1
- package/dist/jobs/actions/manage-recurring-job.d.ts +2 -0
- package/dist/jobs/actions/manage-recurring-job.js +30 -9
- package/dist/jobs/actions/manage-recurring-job.js.map +1 -1
- package/dist/jobs/background-automation-runner.d.ts.map +1 -1
- package/dist/jobs/background-automation-runner.js +60 -0
- package/dist/jobs/background-automation-runner.js.map +1 -1
- package/dist/jobs/cron.d.ts +16 -2
- package/dist/jobs/cron.d.ts.map +1 -1
- package/dist/jobs/cron.js +43 -6
- package/dist/jobs/cron.js.map +1 -1
- package/dist/jobs/frontmatter.d.ts +12 -0
- package/dist/jobs/frontmatter.d.ts.map +1 -1
- package/dist/jobs/frontmatter.js +8 -0
- package/dist/jobs/frontmatter.js.map +1 -1
- package/dist/jobs/run-history.d.ts +56 -0
- package/dist/jobs/run-history.d.ts.map +1 -0
- package/dist/jobs/run-history.js +169 -0
- package/dist/jobs/run-history.js.map +1 -0
- package/dist/jobs/scheduler.d.ts.map +1 -1
- package/dist/jobs/scheduler.js +51 -18
- package/dist/jobs/scheduler.js.map +1 -1
- package/dist/jobs/tools.d.ts.map +1 -1
- package/dist/jobs/tools.js +37 -6
- package/dist/jobs/tools.js.map +1 -1
- package/dist/localization/actions/get-localization-preference.d.ts +1 -2
- package/dist/localization/actions/get-localization-preference.d.ts.map +1 -1
- package/dist/localization/actions/get-localization-preference.js.map +1 -1
- package/dist/localization/actions/set-localization-preference.d.ts +3 -3
- package/dist/localization/actions/set-localization-preference.d.ts.map +1 -1
- package/dist/localization/actions/set-localization-preference.js +33 -8
- package/dist/localization/actions/set-localization-preference.js.map +1 -1
- package/dist/localization/default-messages.d.ts +25 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +25 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/localization/shared.d.ts +16 -1
- package/dist/localization/shared.d.ts.map +1 -1
- package/dist/localization/shared.js +22 -4
- package/dist/localization/shared.js.map +1 -1
- package/dist/localization/user-timezone.d.ts +11 -0
- package/dist/localization/user-timezone.d.ts.map +1 -0
- package/dist/localization/user-timezone.js +31 -0
- package/dist/localization/user-timezone.js.map +1 -0
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +1 -1
- package/dist/org/app-roles-handlers.d.ts.map +1 -1
- package/dist/org/app-roles-handlers.js +1 -8
- package/dist/org/app-roles-handlers.js.map +1 -1
- package/dist/org/index.d.ts +1 -0
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +1 -0
- package/dist/org/index.js.map +1 -1
- package/dist/org/membership.d.ts +6 -0
- package/dist/org/membership.d.ts.map +1 -0
- package/dist/org/membership.js +16 -0
- package/dist/org/membership.js.map +1 -0
- package/dist/provider-api/actions/custom-provider-registration.d.ts +10 -10
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/review/actions/create-review-comment.d.ts +32 -1
- package/dist/review/actions/create-review-comment.d.ts.map +1 -1
- package/dist/review/actions/create-review-comment.js +3 -1
- package/dist/review/actions/create-review-comment.js.map +1 -1
- package/dist/review/actions/reply-review-comment.d.ts +32 -1
- package/dist/review/actions/reply-review-comment.d.ts.map +1 -1
- package/dist/review/actions/reply-review-comment.js +3 -1
- package/dist/review/actions/reply-review-comment.js.map +1 -1
- package/dist/review/index.d.ts +1 -0
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +1 -0
- package/dist/review/index.js.map +1 -1
- package/dist/review/notifications.d.ts +24 -0
- package/dist/review/notifications.d.ts.map +1 -0
- package/dist/review/notifications.js +123 -0
- package/dist/review/notifications.js.map +1 -0
- package/dist/review/types.d.ts +5 -0
- package/dist/review/types.d.ts.map +1 -1
- package/dist/review/types.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/activity-notifications.d.ts +61 -0
- package/dist/server/activity-notifications.d.ts.map +1 -0
- package/dist/server/activity-notifications.js +103 -0
- package/dist/server/activity-notifications.js.map +1 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/app-name.d.ts +18 -0
- package/dist/server/app-name.d.ts.map +1 -1
- package/dist/server/app-name.js +31 -0
- package/dist/server/app-name.js.map +1 -1
- package/dist/server/better-auth-instance.js +4 -4
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/email-templates.d.ts +12 -0
- package/dist/server/email-templates.d.ts.map +1 -1
- package/dist/server/email-templates.js +57 -14
- package/dist/server/email-templates.js.map +1 -1
- package/dist/server/email.d.ts +12 -0
- package/dist/server/email.d.ts.map +1 -1
- package/dist/server/email.js +52 -5
- package/dist/server/email.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/sharing/index.d.ts +1 -0
- package/dist/sharing/index.d.ts.map +1 -1
- package/dist/sharing/index.js +1 -0
- package/dist/sharing/index.js.map +1 -1
- package/dist/sharing/recipients.d.ts +31 -0
- package/dist/sharing/recipients.d.ts.map +1 -0
- package/dist/sharing/recipients.js +40 -0
- package/dist/sharing/recipients.js.map +1 -0
- package/dist/templates/chat/app/routes/settings.tsx +14 -22
- package/dist/templates/chat/vitest.config.ts +9 -0
- package/dist/triggers/actions/list-automations.d.ts +2 -0
- package/dist/triggers/actions/list-automations.d.ts.map +1 -1
- package/dist/triggers/actions/list-automations.js +19 -8
- package/dist/triggers/actions/list-automations.js.map +1 -1
- package/dist/triggers/actions/manage-automation.d.ts +6 -0
- package/dist/triggers/actions/manage-automation.js +12 -6
- package/dist/triggers/actions/manage-automation.js.map +1 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +15 -4
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/dispatcher.d.ts.map +1 -1
- package/dist/triggers/dispatcher.js +19 -17
- package/dist/triggers/dispatcher.js.map +1 -1
- package/dist/triggers/routes.d.ts.map +1 -1
- package/dist/triggers/routes.js +6 -6
- package/dist/triggers/routes.js.map +1 -1
- package/dist/vitest-config.d.ts +10 -0
- package/dist/vitest-config.d.ts.map +1 -0
- package/dist/vitest-config.js +55 -0
- package/dist/vitest-config.js.map +1 -0
- package/package.json +5 -4
- package/src/automations/service.ts +41 -4
- package/src/cli/mcp-config-writers.ts +27 -21
- package/src/client/agent-page/AgentJobsTab.tsx +187 -85
- package/src/client/agent-page/AutomationDetailsDialog.tsx +208 -0
- package/src/client/agent-page/AutomationScheduleDialog.tsx +180 -0
- package/src/client/agent-page/TimezoneSelect.tsx +241 -0
- package/src/client/agent-page/use-jobs.ts +50 -10
- package/src/client/org/OrgSwitcher.tsx +26 -0
- package/src/client/settings/AccountSettingsCard.tsx +35 -31
- package/src/client/settings/SchedulingTimezoneField.tsx +71 -0
- package/src/client/settings/SettingsRow.tsx +113 -0
- package/src/client/settings/agent-settings-search.ts +2 -1
- package/src/client/settings/index.ts +6 -0
- package/src/integrations/identity.ts +2 -12
- package/src/jobs/actions/list-automation-runs.ts +33 -0
- package/src/jobs/actions/list-recurring-jobs.ts +27 -6
- package/src/jobs/actions/manage-recurring-job.ts +41 -9
- package/src/jobs/background-automation-runner.ts +88 -0
- package/src/jobs/cron.ts +52 -6
- package/src/jobs/frontmatter.ts +20 -0
- package/src/jobs/run-history.ts +242 -0
- package/src/jobs/scheduler.ts +73 -18
- package/src/jobs/tools.ts +54 -6
- package/src/localization/actions/get-localization-preference.ts +2 -2
- package/src/localization/actions/set-localization-preference.ts +49 -11
- package/src/localization/default-messages.ts +27 -0
- package/src/localization/shared.ts +39 -6
- package/src/localization/user-timezone.ts +36 -0
- package/src/org/app-roles-handlers.ts +1 -9
- package/src/org/index.ts +2 -0
- package/src/org/membership.ts +18 -0
- package/src/review/actions/create-review-comment.ts +4 -1
- package/src/review/actions/reply-review-comment.ts +4 -1
- package/src/review/index.ts +5 -0
- package/src/review/notifications.ts +163 -0
- package/src/review/types.ts +7 -0
- package/src/server/action-discovery.ts +4 -0
- package/src/server/activity-notifications.ts +174 -0
- package/src/server/app-name.ts +31 -0
- package/src/server/better-auth-instance.ts +4 -4
- package/src/server/email-templates.ts +70 -14
- package/src/server/email.ts +66 -3
- package/src/server/index.ts +10 -0
- package/src/sharing/index.ts +5 -0
- package/src/sharing/recipients.ts +67 -0
- package/src/templates/chat/app/routes/settings.tsx +14 -22
- package/src/templates/chat/vitest.config.ts +9 -0
- package/src/triggers/actions/list-automations.ts +27 -8
- package/src/triggers/actions/manage-automation.ts +17 -6
- package/src/triggers/actions.ts +16 -4
- package/src/triggers/dispatcher.ts +44 -30
- package/src/triggers/routes.ts +19 -6
- package/src/vitest-config.ts +68 -0
|
@@ -3,6 +3,8 @@ import { LanguagePicker, useT } from "@agent-native/core/client/i18n";
|
|
|
3
3
|
import { TeamPage } from "@agent-native/core/client/org";
|
|
4
4
|
import {
|
|
5
5
|
AccountSettingsCard,
|
|
6
|
+
SettingsGroup,
|
|
7
|
+
SettingsRow,
|
|
6
8
|
SettingsTabsPage,
|
|
7
9
|
useAgentSettingsTabs,
|
|
8
10
|
type SettingsSearchEntry,
|
|
@@ -10,14 +12,6 @@ import {
|
|
|
10
12
|
import { useSetPageTitle } from "@agent-native/toolkit/app-shell";
|
|
11
13
|
import { useMemo } from "react";
|
|
12
14
|
|
|
13
|
-
import {
|
|
14
|
-
Card,
|
|
15
|
-
CardContent,
|
|
16
|
-
CardDescription,
|
|
17
|
-
CardHeader,
|
|
18
|
-
CardTitle,
|
|
19
|
-
} from "@/components/ui/card";
|
|
20
|
-
import { Label } from "@/components/ui/label";
|
|
21
15
|
import { APP_TITLE } from "@/lib/app-config";
|
|
22
16
|
|
|
23
17
|
import changelog from "../../CHANGELOG.md?raw";
|
|
@@ -55,20 +49,18 @@ export default function SettingsRoute() {
|
|
|
55
49
|
{t("settings.description")}
|
|
56
50
|
</p>
|
|
57
51
|
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
</CardContent>
|
|
71
|
-
</Card>
|
|
52
|
+
<SettingsGroup>
|
|
53
|
+
<SettingsRow
|
|
54
|
+
id="language"
|
|
55
|
+
label={t("settings.languageTitle")}
|
|
56
|
+
description={t("settings.languageDescription")}
|
|
57
|
+
control={
|
|
58
|
+
<div className="w-56">
|
|
59
|
+
<LanguagePicker label={t("settings.languageLabel")} />
|
|
60
|
+
</div>
|
|
61
|
+
}
|
|
62
|
+
/>
|
|
63
|
+
</SettingsGroup>
|
|
72
64
|
</div>
|
|
73
65
|
}
|
|
74
66
|
team={
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import baseConfig from "@agent-native/core/vitest-config";
|
|
2
|
+
import { mergeConfig } from "vitest/config";
|
|
3
|
+
|
|
4
|
+
import viteConfig from "./vite.config";
|
|
5
|
+
|
|
6
|
+
// Kept separate from vite.config.ts so the production config never imports
|
|
7
|
+
// vitest: `agent-native build` loads vite.config.ts in installs where vitest
|
|
8
|
+
// is absent.
|
|
9
|
+
export default mergeConfig(viteConfig, baseConfig);
|
|
@@ -229,6 +229,37 @@ downsamples to a 2000-point peaks array. Peaks are cached in
|
|
|
229
229
|
recompute. The editor reads the cache first and falls back to computing only
|
|
230
230
|
when the key is missing or corrupted.
|
|
231
231
|
|
|
232
|
+
### Timeline filmstrip
|
|
233
|
+
|
|
234
|
+
The trim track shows video frames behind the waveform. Two paths, and the order
|
|
235
|
+
matters:
|
|
236
|
+
|
|
237
|
+
1. **Sprite (preferred).** `generate-filmstrip` runs one ffmpeg pass
|
|
238
|
+
(`fps` → `scale` → `pad` → `tile`) into a single JPEG grid, uploads it, and
|
|
239
|
+
stores the URL plus grid geometry on the recording. The editor renders cells
|
|
240
|
+
with CSS `background-position` — one cached image request, no video decoding
|
|
241
|
+
in the browser.
|
|
242
|
+
2. **Browser extraction (fallback).** `extractFilmstripThumbnails()` seeks a
|
|
243
|
+
detached `<video>` once per frame. Only used when there is no sprite, which
|
|
244
|
+
means hosts without ffmpeg and local/dev media the server cannot fetch.
|
|
245
|
+
|
|
246
|
+
Three traps this design exists to avoid:
|
|
247
|
+
|
|
248
|
+
- **Pass the proxied URL.** Frame extraction must use `getWaveformMediaUrl()`,
|
|
249
|
+
never `recording.videoUrl` — reading pixels back out of a cross-origin video
|
|
250
|
+
taints the canvas and `toDataURL` throws, so provider media silently yields no
|
|
251
|
+
filmstrip at all.
|
|
252
|
+
- **Both paths sample cell midpoints,** not `0 … duration` endpoints. The strip
|
|
253
|
+
renders N equal cells, so cell `i` must show the middle of the slot it
|
|
254
|
+
occupies or every thumbnail sits up to half a cell from the time beneath it.
|
|
255
|
+
- **Cell count comes from the geometry,** `trackWidth / (height × aspect)`, not
|
|
256
|
+
from the sprite's frame count. Rendering all 40 frames across an unzoomed
|
|
257
|
+
track makes each cell portrait, and `object-cover` then shows a narrow centre
|
|
258
|
+
slice of each frame instead of a recognisable thumbnail.
|
|
259
|
+
|
|
260
|
+
The filmstrip never replaces the waveform — peaks still draw on top of a scrim,
|
|
261
|
+
because frames say nothing about the audio.
|
|
262
|
+
|
|
232
263
|
### Keyboard shortcuts (editor scope)
|
|
233
264
|
|
|
234
265
|
- **Space** — play / pause (even while focused in the editor area)
|
|
@@ -92,6 +92,7 @@ Read the matching skill before deeper work in that area:
|
|
|
92
92
|
| `move-recording` | Move `id` or `ids` to a folder or root |
|
|
93
93
|
| `archive-`, `trash-`, `restore-recording` | Lifecycle |
|
|
94
94
|
| `reprocess-recording` | Repair unseekable/frozen media |
|
|
95
|
+
| `generate-filmstrip` | Editor timeline frame sprite; `all` backfills |
|
|
95
96
|
| `request-transcript`, `cleanup-transcript` | Transcribe; `force`/`regenerate` |
|
|
96
97
|
| `regenerate-title`, `-summary`, `-chapters` | AI metadata |
|
|
97
98
|
| `trim-`, `split-recording`, `remove-silences`, `remove-filler-words` | Edits |
|
|
@@ -15,6 +15,7 @@ import { eq } from "drizzle-orm";
|
|
|
15
15
|
import { z } from "zod";
|
|
16
16
|
|
|
17
17
|
import { getDb, schema } from "../server/db/index.js";
|
|
18
|
+
import { notifyRecordingComment } from "../server/lib/activity-notifications.js";
|
|
18
19
|
import { nanoid } from "../server/lib/recordings.js";
|
|
19
20
|
|
|
20
21
|
export default defineAction({
|
|
@@ -80,12 +81,22 @@ export default defineAction({
|
|
|
80
81
|
updatedAt: now,
|
|
81
82
|
});
|
|
82
83
|
|
|
84
|
+
const notified = await notifyRecordingComment({
|
|
85
|
+
recordingId: args.recordingId,
|
|
86
|
+
threadId,
|
|
87
|
+
authorEmail,
|
|
88
|
+
authorName: args.authorName,
|
|
89
|
+
content: args.content,
|
|
90
|
+
videoTimestampMs: args.videoTimestampMs,
|
|
91
|
+
isReply: Boolean(parentId),
|
|
92
|
+
});
|
|
93
|
+
|
|
83
94
|
await writeAppState("refresh-signal", { ts: Date.now() });
|
|
84
95
|
|
|
85
96
|
console.log(
|
|
86
97
|
`Added comment to recording ${args.recordingId} @ ${args.videoTimestampMs}ms (thread: ${threadId})`,
|
|
87
98
|
);
|
|
88
99
|
|
|
89
|
-
return { id, threadId };
|
|
100
|
+
return { id, threadId, notified };
|
|
90
101
|
},
|
|
91
102
|
});
|
|
@@ -17,7 +17,7 @@ import { getDb, schema } from "../server/db/index.js";
|
|
|
17
17
|
import {
|
|
18
18
|
getCurrentOwnerEmail,
|
|
19
19
|
getActiveOrganizationId,
|
|
20
|
-
|
|
20
|
+
getDefaultRecordingVisibility,
|
|
21
21
|
nanoid,
|
|
22
22
|
} from "../server/lib/recordings.js";
|
|
23
23
|
|
|
@@ -191,7 +191,7 @@ export default defineAction({
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
const visibility =
|
|
194
|
-
args.visibility ?? (await
|
|
194
|
+
args.visibility ?? (await getDefaultRecordingVisibility(orgId));
|
|
195
195
|
|
|
196
196
|
try {
|
|
197
197
|
await db.insert(schema.meetings).values({
|
|
@@ -20,7 +20,7 @@ import { createError } from "h3";
|
|
|
20
20
|
import { getDb, schema } from "../server/db/index.js";
|
|
21
21
|
import {
|
|
22
22
|
getCurrentOwnerEmail,
|
|
23
|
-
|
|
23
|
+
getDefaultRecordingVisibility,
|
|
24
24
|
nanoid,
|
|
25
25
|
requireOrganizationAccess,
|
|
26
26
|
stringifySpaceIds,
|
|
@@ -53,7 +53,7 @@ export default defineAction({
|
|
|
53
53
|
args.organizationId,
|
|
54
54
|
);
|
|
55
55
|
const defaultVisibility =
|
|
56
|
-
await
|
|
56
|
+
await getDefaultRecordingVisibility(organizationId);
|
|
57
57
|
|
|
58
58
|
const spaceIds = (args.spaceIds ?? []).filter(
|
|
59
59
|
(value, index, arr) => value && arr.indexOf(value) === index,
|
|
@@ -902,6 +902,12 @@ export default defineAction({
|
|
|
902
902
|
"Whether the uploaded video bytes were already locally transcoded/compressed before upload",
|
|
903
903
|
),
|
|
904
904
|
mediaVerificationRetryAttempt: z.number().int().min(1).max(10).optional(),
|
|
905
|
+
uploadGenerationId: z
|
|
906
|
+
.string()
|
|
907
|
+
.min(1)
|
|
908
|
+
.max(128)
|
|
909
|
+
.optional()
|
|
910
|
+
.describe("Upload generation that owns the scratch data being finalized"),
|
|
905
911
|
}),
|
|
906
912
|
run: async (args) => {
|
|
907
913
|
const db = getDb();
|
|
@@ -921,7 +927,7 @@ export default defineAction({
|
|
|
921
927
|
// never retain scratch video blobs.
|
|
922
928
|
let chunkKeysToPurge: string[] = [];
|
|
923
929
|
try {
|
|
924
|
-
|
|
930
|
+
let [existing] = await db
|
|
925
931
|
.select()
|
|
926
932
|
.from(schema.recordings)
|
|
927
933
|
.where(
|
|
@@ -938,6 +944,32 @@ export default defineAction({
|
|
|
938
944
|
throw new Error(`Recording not found: ${id}`);
|
|
939
945
|
}
|
|
940
946
|
|
|
947
|
+
const generationId = args.uploadGenerationId ?? null;
|
|
948
|
+
if ((existing.uploadGenerationId ?? null) !== generationId) {
|
|
949
|
+
throw new Error("Upload generation changed before finalization");
|
|
950
|
+
}
|
|
951
|
+
// Claim finalization before touching provider/scratch state. Reset only
|
|
952
|
+
// admits uploading/failed rows, so once this CAS succeeds it cannot
|
|
953
|
+
// replace the generation underneath a delayed final chunk.
|
|
954
|
+
if (generationId !== null && existing.status === "uploading") {
|
|
955
|
+
const claimed = await db
|
|
956
|
+
.update(schema.recordings)
|
|
957
|
+
.set({ status: "processing", updatedAt: new Date().toISOString() })
|
|
958
|
+
.where(
|
|
959
|
+
and(
|
|
960
|
+
eq(schema.recordings.id, id),
|
|
961
|
+
ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
|
|
962
|
+
eq(schema.recordings.status, "uploading"),
|
|
963
|
+
eq(schema.recordings.uploadGenerationId, generationId),
|
|
964
|
+
),
|
|
965
|
+
)
|
|
966
|
+
.returning();
|
|
967
|
+
if (claimed.length !== 1) {
|
|
968
|
+
throw new Error("Upload changed before finalization could claim it");
|
|
969
|
+
}
|
|
970
|
+
existing = claimed[0]!;
|
|
971
|
+
}
|
|
972
|
+
|
|
941
973
|
// Idempotency guard: finalize can be re-invoked when a client retries the
|
|
942
974
|
// final chunk after a lost response. If already 'ready' return the existing
|
|
943
975
|
// result instead of re-running the complete/assembly path (session and
|
|
@@ -947,7 +979,7 @@ export default defineAction({
|
|
|
947
979
|
// A prior attempt may have persisted the ready row and then failed
|
|
948
980
|
// before deleting its resumable-session handle. The provider upload is
|
|
949
981
|
// complete at this point, so retire only the local retry state.
|
|
950
|
-
await deleteResumableSession(id).catch((err) =>
|
|
982
|
+
await deleteResumableSession(id, generationId).catch((err) =>
|
|
951
983
|
console.warn("[finalize] failed to delete resumable session:", err),
|
|
952
984
|
);
|
|
953
985
|
await deleteAppState(mediaVerificationStateKey(id)).catch(() => {});
|
|
@@ -1038,7 +1070,7 @@ export default defineAction({
|
|
|
1038
1070
|
|
|
1039
1071
|
// Resumable path: create-recording initialized a session and chunk.post.ts
|
|
1040
1072
|
// forwarded all chunks to the provider. Complete the session to get the CDN URL.
|
|
1041
|
-
const resumableSession = await getResumableSession(id);
|
|
1073
|
+
const resumableSession = await getResumableSession(id, generationId);
|
|
1042
1074
|
if (resumableSession && isStreamingUploadDisabled()) {
|
|
1043
1075
|
console.warn(
|
|
1044
1076
|
`[finalize] streaming uploads are disabled, but completing existing resumable session for in-flight recording: ${id}`,
|
|
@@ -1147,7 +1179,7 @@ export default defineAction({
|
|
|
1147
1179
|
locallyTranscoded: args.locallyTranscoded === true,
|
|
1148
1180
|
},
|
|
1149
1181
|
});
|
|
1150
|
-
await deleteResumableSession(id).catch((deleteErr) =>
|
|
1182
|
+
await deleteResumableSession(id, generationId).catch((deleteErr) =>
|
|
1151
1183
|
console.warn(
|
|
1152
1184
|
"[finalize] failed to retire pending resumable session:",
|
|
1153
1185
|
deleteErr,
|
|
@@ -1178,7 +1210,7 @@ export default defineAction({
|
|
|
1178
1210
|
}
|
|
1179
1211
|
// Delete only after durable state is written — so a retry before
|
|
1180
1212
|
// this point can still find the session and re-enter this path.
|
|
1181
|
-
deleteResumableSession(id).catch((err) =>
|
|
1213
|
+
deleteResumableSession(id, generationId).catch((err) =>
|
|
1182
1214
|
console.warn("[finalize] failed to delete resumable session:", err),
|
|
1183
1215
|
);
|
|
1184
1216
|
return result;
|
|
@@ -1262,7 +1294,11 @@ export default defineAction({
|
|
|
1262
1294
|
// Pull chunk keys first, then fetch values one at a time. A single
|
|
1263
1295
|
// SELECT key,value over many base64 chunks can exceed Neon's 8s op
|
|
1264
1296
|
// timeout before we even start assembling the recording.
|
|
1265
|
-
const chunkKeys = await listRecordingChunkKeys(
|
|
1297
|
+
const chunkKeys = await listRecordingChunkKeys(
|
|
1298
|
+
ownerEmail,
|
|
1299
|
+
id,
|
|
1300
|
+
generationId,
|
|
1301
|
+
);
|
|
1266
1302
|
const expectedDataChunks = stateNumber(uploadState, "expectedDataChunks");
|
|
1267
1303
|
debugLog("[finalize] chunks found", {
|
|
1268
1304
|
id,
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate editor-timeline filmstrip sprites for stored recordings.
|
|
3
|
+
*
|
|
4
|
+
* The editor shows video frames behind the waveform so a trim range can be
|
|
5
|
+
* found visually. Generating them server-side with one ffmpeg pass replaces
|
|
6
|
+
* per-frame decoding in the browser, which cannot read cross-origin media at
|
|
7
|
+
* all and needs one seek per frame.
|
|
8
|
+
*
|
|
9
|
+
* Non-destructive and idempotent: a recording whose sprite already matches its
|
|
10
|
+
* current media is skipped, and any failure leaves the row untouched so the
|
|
11
|
+
* editor keeps its browser-side fallback.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* pnpm action generate-filmstrip --id=<recordingId>
|
|
15
|
+
* pnpm action generate-filmstrip --id=<recordingId> --force
|
|
16
|
+
* pnpm action generate-filmstrip --all --limit=20
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { defineAction } from "@agent-native/core";
|
|
20
|
+
import { and, desc, eq, isNotNull, isNull } from "drizzle-orm";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
|
|
23
|
+
import { getDb, schema } from "../server/db/index.js";
|
|
24
|
+
import {
|
|
25
|
+
getCurrentOwnerEmail,
|
|
26
|
+
ownerEmailMatches,
|
|
27
|
+
} from "../server/lib/recordings.js";
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_FILMSTRIP_FRAME_COUNT,
|
|
30
|
+
MAX_FILMSTRIP_FRAME_COUNT,
|
|
31
|
+
} from "../server/lib/video-filmstrip-sprite.js";
|
|
32
|
+
import {
|
|
33
|
+
ensureRecordingFilmstrip,
|
|
34
|
+
type EnsureFilmstripResult,
|
|
35
|
+
} from "./lib/ensure-recording-filmstrip.js";
|
|
36
|
+
|
|
37
|
+
const MAX_TARGETS_PER_CALL = 50;
|
|
38
|
+
const DEFAULT_ALL_LIMIT = 10;
|
|
39
|
+
const cliBoolean = z.preprocess((value) => {
|
|
40
|
+
if (value === "true" || value === "1") return true;
|
|
41
|
+
if (value === "false" || value === "0") return false;
|
|
42
|
+
return value;
|
|
43
|
+
}, z.boolean());
|
|
44
|
+
|
|
45
|
+
export default defineAction({
|
|
46
|
+
description:
|
|
47
|
+
"Generate the editor timeline filmstrip (a sprite of evenly-spaced video frames) for one or more of the caller's recordings, so the editor can show frames behind the waveform without decoding video in the browser. Pass `id` for one clip, `ids` for several, or `all: true` to backfill clips that have no filmstrip yet. Recordings whose sprite already matches their current media are skipped unless `force` is true. Requires ffmpeg on the server; without it this reports `skipped-no-ffmpeg` and the editor falls back to browser-side extraction.",
|
|
48
|
+
schema: z.object({
|
|
49
|
+
id: z
|
|
50
|
+
.string()
|
|
51
|
+
.optional()
|
|
52
|
+
.describe("A single recording id to generate a filmstrip for."),
|
|
53
|
+
ids: z
|
|
54
|
+
.array(z.string())
|
|
55
|
+
.optional()
|
|
56
|
+
.describe("Several recording ids to process in one call."),
|
|
57
|
+
all: cliBoolean
|
|
58
|
+
.optional()
|
|
59
|
+
.describe(
|
|
60
|
+
"Backfill the caller's ready clips that have no filmstrip yet, most recent first, up to `limit`.",
|
|
61
|
+
),
|
|
62
|
+
limit: z.coerce
|
|
63
|
+
.number()
|
|
64
|
+
.int()
|
|
65
|
+
.min(1)
|
|
66
|
+
.max(MAX_TARGETS_PER_CALL)
|
|
67
|
+
.optional()
|
|
68
|
+
.describe(
|
|
69
|
+
`Max clips to process when using \`all\` (default ${DEFAULT_ALL_LIMIT}).`,
|
|
70
|
+
),
|
|
71
|
+
frameCount: z.coerce
|
|
72
|
+
.number()
|
|
73
|
+
.int()
|
|
74
|
+
.min(4)
|
|
75
|
+
.max(MAX_FILMSTRIP_FRAME_COUNT)
|
|
76
|
+
.optional()
|
|
77
|
+
.describe(
|
|
78
|
+
`How many frames to sample across the clip (default ${DEFAULT_FILMSTRIP_FRAME_COUNT}). More frames means finer scrubbing detail and a larger sprite.`,
|
|
79
|
+
),
|
|
80
|
+
force: cliBoolean
|
|
81
|
+
.optional()
|
|
82
|
+
.describe("Regenerate even when a current filmstrip already exists."),
|
|
83
|
+
}),
|
|
84
|
+
run: async (args) => {
|
|
85
|
+
const db = getDb();
|
|
86
|
+
const ownerEmail = getCurrentOwnerEmail();
|
|
87
|
+
|
|
88
|
+
let targetIds: string[] = [];
|
|
89
|
+
if (args.id) targetIds.push(args.id);
|
|
90
|
+
if (args.ids?.length) targetIds.push(...args.ids.filter(Boolean));
|
|
91
|
+
|
|
92
|
+
if (args.all) {
|
|
93
|
+
const conditions = [
|
|
94
|
+
ownerEmailMatches(schema.recordings.ownerEmail, ownerEmail),
|
|
95
|
+
eq(schema.recordings.status, "ready"),
|
|
96
|
+
isNotNull(schema.recordings.videoUrl),
|
|
97
|
+
];
|
|
98
|
+
if (!args.force) {
|
|
99
|
+
conditions.push(isNull(schema.recordings.filmstripUrl));
|
|
100
|
+
}
|
|
101
|
+
const rows = await db
|
|
102
|
+
.select({ id: schema.recordings.id })
|
|
103
|
+
.from(schema.recordings)
|
|
104
|
+
.where(and(...conditions))
|
|
105
|
+
.orderBy(desc(schema.recordings.createdAt))
|
|
106
|
+
.limit(args.limit ?? DEFAULT_ALL_LIMIT);
|
|
107
|
+
targetIds.push(...rows.map((r) => r.id));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// De-dupe while preserving order, and bound the batch so one call can't run
|
|
111
|
+
// unboundedly under the hosted foreground budget.
|
|
112
|
+
targetIds = Array.from(new Set(targetIds)).slice(0, MAX_TARGETS_PER_CALL);
|
|
113
|
+
|
|
114
|
+
if (targetIds.length === 0) {
|
|
115
|
+
return {
|
|
116
|
+
ok: true,
|
|
117
|
+
processed: 0,
|
|
118
|
+
changed: 0,
|
|
119
|
+
results: [] as EnsureFilmstripResult[],
|
|
120
|
+
message:
|
|
121
|
+
"No recordings to process. Pass id / ids, or all: true to backfill clips without a filmstrip.",
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const results: EnsureFilmstripResult[] = [];
|
|
126
|
+
for (const recordingId of targetIds) {
|
|
127
|
+
try {
|
|
128
|
+
results.push(
|
|
129
|
+
await ensureRecordingFilmstrip({
|
|
130
|
+
recordingId,
|
|
131
|
+
ownerEmail,
|
|
132
|
+
frameCount: args.frameCount,
|
|
133
|
+
force: Boolean(args.force),
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.warn("[generate-filmstrip] failed for", recordingId, err);
|
|
138
|
+
results.push({
|
|
139
|
+
recordingId,
|
|
140
|
+
status: "failed-ffmpeg",
|
|
141
|
+
changed: false,
|
|
142
|
+
detail: err instanceof Error ? err.message : String(err),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const changed = results.filter((r) => r.changed).length;
|
|
148
|
+
console.log(
|
|
149
|
+
`Processed ${results.length} recording(s); ${changed} filmstrip(s) generated.`,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
return { ok: true, processed: results.length, changed, results };
|
|
153
|
+
},
|
|
154
|
+
});
|
|
@@ -314,6 +314,12 @@ export default defineAction({
|
|
|
314
314
|
description: rec.description,
|
|
315
315
|
thumbnailUrl: rec.thumbnailUrl,
|
|
316
316
|
animatedThumbnailUrl: rec.animatedThumbnailUrl,
|
|
317
|
+
filmstripUrl: rec.filmstripUrl ?? null,
|
|
318
|
+
filmstripFrameCount: rec.filmstripFrameCount ?? 0,
|
|
319
|
+
filmstripColumns: rec.filmstripColumns ?? 0,
|
|
320
|
+
filmstripRows: rec.filmstripRows ?? 0,
|
|
321
|
+
filmstripFrameWidth: rec.filmstripFrameWidth ?? 0,
|
|
322
|
+
filmstripFrameHeight: rec.filmstripFrameHeight ?? 0,
|
|
317
323
|
sourceAppName: rec.sourceAppName,
|
|
318
324
|
sourceWindowTitle: rec.sourceWindowTitle,
|
|
319
325
|
durationMs: rec.durationMs,
|
|
@@ -12,7 +12,7 @@ import { queueBuilderMediaCompression } from "../server/lib/builder-media-compre
|
|
|
12
12
|
import { dispatchPostFinalizeJob } from "../server/lib/post-finalize-dispatch.js";
|
|
13
13
|
import {
|
|
14
14
|
getCurrentOwnerEmail,
|
|
15
|
-
|
|
15
|
+
getDefaultRecordingVisibility,
|
|
16
16
|
nanoid,
|
|
17
17
|
ownerEmailMatches,
|
|
18
18
|
parseSpaceIds,
|
|
@@ -215,7 +215,7 @@ export default defineAction({
|
|
|
215
215
|
existingRecording?.organizationId ?? args.organizationId,
|
|
216
216
|
);
|
|
217
217
|
const defaultVisibility =
|
|
218
|
-
await
|
|
218
|
+
await getDefaultRecordingVisibility(organizationId);
|
|
219
219
|
|
|
220
220
|
const now = new Date().toISOString();
|
|
221
221
|
const id = existingRecording?.id ?? nanoid();
|