pixivflow 2.18.0 → 2.19.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/dist/commands/SchedulerCommand.js +105 -45
- package/dist/commands/SchedulerIdleLifecycle.d.ts +82 -0
- package/dist/commands/SchedulerIdleLifecycle.js +135 -0
- package/dist/commands/scheduler-runtime.d.ts +6 -0
- package/dist/commands/scheduler-runtime.js +77 -9
- package/dist/config/types.d.ts +33 -0
- package/dist/config/validation.js +10 -1
- package/dist/download/DownloadManager.d.ts +9 -0
- package/dist/download/DownloadManager.js +18 -1
- package/dist/package.json +1 -1
- package/dist/pixiv-client/PixivFlowPixivClient.d.ts +7 -1
- package/dist/pixiv-client/PixivFlowPixivClient.js +14 -8
- package/dist/pixiv-client/TargetSearchRunner.d.ts +2 -2
- package/dist/pixiv-client/TargetSearchRunner.js +21 -14
- package/dist/scheduler/MultiScheduleManager.d.ts +33 -1
- package/dist/scheduler/MultiScheduleManager.js +115 -2
- package/dist/scheduler/ScheduleTriggerServer.d.ts +32 -9
- package/dist/scheduler/ScheduleTriggerServer.js +34 -6
- package/dist/scheduler/Scheduler.d.ts +28 -1
- package/dist/scheduler/Scheduler.js +82 -7
- package/dist/scheduler/SlotCoordinator.d.ts +28 -3
- package/dist/scheduler/SlotCoordinator.js +31 -5
- package/dist/storage/repositories/SlotRepository.d.ts +28 -0
- package/dist/storage/repositories/SlotRepository.js +42 -4
- package/dist/topic/TopicPipeline.d.ts +15 -1
- package/dist/topic/TopicPipeline.js +18 -2
- package/dist/topic/TopicResolver.d.ts +5 -1
- package/dist/topic/TopicResolver.js +32 -6
- package/dist/topic/createTopicPipeline.d.ts +2 -2
- package/dist/topic/createTopicPipeline.js +5 -5
- package/dist/topic/types.d.ts +5 -1
- package/dist/utils/errors.d.ts +18 -0
- package/dist/utils/errors.js +30 -0
- package/dist/version.js +1 -1
- package/dist/webui/package.json +1 -1
- package/package.json +2 -1
- package/dist/auth/PixivAuth.d.ts.map +0 -1
- package/dist/auth/PixivAuth.js.map +0 -1
- package/dist/batch/executionResult.d.ts.map +0 -1
- package/dist/batch/executionResult.js.map +0 -1
- package/dist/batch/runDiagnostics.d.ts.map +0 -1
- package/dist/batch/runDiagnostics.js.map +0 -1
- package/dist/cli/ArgumentParser.d.ts.map +0 -1
- package/dist/cli/ArgumentParser.js.map +0 -1
- package/dist/cli/InteractivePrompt.d.ts.map +0 -1
- package/dist/cli/InteractivePrompt.js.map +0 -1
- package/dist/commands/BackupCommand.d.ts.map +0 -1
- package/dist/commands/BackupCommand.js.map +0 -1
- package/dist/commands/Command.d.ts.map +0 -1
- package/dist/commands/Command.js.map +0 -1
- package/dist/commands/CommandRegistry.d.ts.map +0 -1
- package/dist/commands/CommandRegistry.js.map +0 -1
- package/dist/commands/ConfigCommand.d.ts.map +0 -1
- package/dist/commands/ConfigCommand.js.map +0 -1
- package/dist/commands/DiagnoseEgressCommand.d.ts.map +0 -1
- package/dist/commands/DiagnoseEgressCommand.js.map +0 -1
- package/dist/commands/DirsCommand.d.ts.map +0 -1
- package/dist/commands/DirsCommand.js.map +0 -1
- package/dist/commands/DoctorCommand.d.ts.map +0 -1
- package/dist/commands/DoctorCommand.js.map +0 -1
- package/dist/commands/DownloadCommand.d.ts.map +0 -1
- package/dist/commands/DownloadCommand.js.map +0 -1
- package/dist/commands/ExecuteSlotCommand.d.ts.map +0 -1
- package/dist/commands/ExecuteSlotCommand.js.map +0 -1
- package/dist/commands/HealthCommand.d.ts.map +0 -1
- package/dist/commands/HealthCommand.js.map +0 -1
- package/dist/commands/HelpCommand.d.ts.map +0 -1
- package/dist/commands/HelpCommand.js.map +0 -1
- package/dist/commands/LoginCommand.d.ts.map +0 -1
- package/dist/commands/LoginCommand.js.map +0 -1
- package/dist/commands/LoginHeadlessCommand.d.ts.map +0 -1
- package/dist/commands/LoginHeadlessCommand.js.map +0 -1
- package/dist/commands/LogsCommand.d.ts.map +0 -1
- package/dist/commands/LogsCommand.js.map +0 -1
- package/dist/commands/MaintainCommand.d.ts.map +0 -1
- package/dist/commands/MaintainCommand.js.map +0 -1
- package/dist/commands/MigrateConfigCommand.d.ts.map +0 -1
- package/dist/commands/MigrateConfigCommand.js.map +0 -1
- package/dist/commands/MonitorCommand.d.ts.map +0 -1
- package/dist/commands/MonitorCommand.js.map +0 -1
- package/dist/commands/NormalizeCommand.d.ts.map +0 -1
- package/dist/commands/NormalizeCommand.js.map +0 -1
- package/dist/commands/OutboxCommand.d.ts.map +0 -1
- package/dist/commands/OutboxCommand.js.map +0 -1
- package/dist/commands/RandomDownloadCommand.d.ts.map +0 -1
- package/dist/commands/RandomDownloadCommand.js.map +0 -1
- package/dist/commands/ReconcileCommand.d.ts.map +0 -1
- package/dist/commands/ReconcileCommand.js.map +0 -1
- package/dist/commands/RefreshCommand.d.ts.map +0 -1
- package/dist/commands/RefreshCommand.js.map +0 -1
- package/dist/commands/RunsCommand.d.ts.map +0 -1
- package/dist/commands/RunsCommand.js.map +0 -1
- package/dist/commands/SchedulerCommand.d.ts.map +0 -1
- package/dist/commands/SchedulerCommand.js.map +0 -1
- package/dist/commands/SchedulerRunOnceCommand.d.ts.map +0 -1
- package/dist/commands/SchedulerRunOnceCommand.js.map +0 -1
- package/dist/commands/SetupCommand.d.ts.map +0 -1
- package/dist/commands/SetupCommand.js.map +0 -1
- package/dist/commands/StatusCommand.d.ts.map +0 -1
- package/dist/commands/StatusCommand.js.map +0 -1
- package/dist/commands/TagsCommand.d.ts.map +0 -1
- package/dist/commands/TagsCommand.js.map +0 -1
- package/dist/commands/TopicCommand.d.ts.map +0 -1
- package/dist/commands/TopicCommand.js.map +0 -1
- package/dist/commands/VersionCommand.d.ts.map +0 -1
- package/dist/commands/VersionCommand.js.map +0 -1
- package/dist/commands/WebUICommand.d.ts.map +0 -1
- package/dist/commands/WebUICommand.js.map +0 -1
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/metadata.d.ts.map +0 -1
- package/dist/commands/metadata.js.map +0 -1
- package/dist/commands/scheduler-runtime.d.ts.map +0 -1
- package/dist/commands/scheduler-runtime.js.map +0 -1
- package/dist/commands/suggestions.d.ts.map +0 -1
- package/dist/commands/suggestions.js.map +0 -1
- package/dist/commands/types.d.ts.map +0 -1
- package/dist/commands/types.js.map +0 -1
- package/dist/config/defaults.d.ts.map +0 -1
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/environment.d.ts.map +0 -1
- package/dist/config/environment.js.map +0 -1
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js.map +0 -1
- package/dist/config/path-resolution.d.ts.map +0 -1
- package/dist/config/path-resolution.js.map +0 -1
- package/dist/config/placeholders.d.ts.map +0 -1
- package/dist/config/placeholders.js.map +0 -1
- package/dist/config/types.d.ts.map +0 -1
- package/dist/config/types.js.map +0 -1
- package/dist/config/validation.d.ts.map +0 -1
- package/dist/config/validation.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/delivery/DeliveryAck.d.ts.map +0 -1
- package/dist/delivery/DeliveryAck.js.map +0 -1
- package/dist/delivery/DeliveryDispatcher.d.ts.map +0 -1
- package/dist/delivery/DeliveryDispatcher.js.map +0 -1
- package/dist/delivery/DeliveryService.d.ts.map +0 -1
- package/dist/delivery/DeliveryService.js.map +0 -1
- package/dist/delivery/HttpMultipartDelivery.d.ts.map +0 -1
- package/dist/delivery/HttpMultipartDelivery.js.map +0 -1
- package/dist/delivery/LegacyOutboxMigration.d.ts.map +0 -1
- package/dist/delivery/LegacyOutboxMigration.js.map +0 -1
- package/dist/delivery/OutboxWorker.d.ts.map +0 -1
- package/dist/delivery/OutboxWorker.js.map +0 -1
- package/dist/delivery/TelegramReviewDelivery.d.ts.map +0 -1
- package/dist/delivery/TelegramReviewDelivery.js.map +0 -1
- package/dist/delivery/errorClass.d.ts.map +0 -1
- package/dist/delivery/errorClass.js.map +0 -1
- package/dist/delivery/types.d.ts.map +0 -1
- package/dist/delivery/types.js.map +0 -1
- package/dist/di/Container.d.ts.map +0 -1
- package/dist/di/Container.js.map +0 -1
- package/dist/di/index.d.ts.map +0 -1
- package/dist/di/index.js.map +0 -1
- package/dist/download/DownloadManager.d.ts.map +0 -1
- package/dist/download/DownloadManager.js.map +0 -1
- package/dist/download/FileNormalizationService.d.ts.map +0 -1
- package/dist/download/FileNormalizationService.js.map +0 -1
- package/dist/download/FileService.d.ts.map +0 -1
- package/dist/download/FileService.js.map +0 -1
- package/dist/download/IllustrationDownloader.d.ts.map +0 -1
- package/dist/download/IllustrationDownloader.js.map +0 -1
- package/dist/download/NovelDownloader.d.ts.map +0 -1
- package/dist/download/NovelDownloader.js.map +0 -1
- package/dist/download/RankingService.d.ts.map +0 -1
- package/dist/download/RankingService.js.map +0 -1
- package/dist/download/UgoiraConverter.d.ts.map +0 -1
- package/dist/download/UgoiraConverter.js.map +0 -1
- package/dist/download/exec/DownloadExecutor.d.ts.map +0 -1
- package/dist/download/exec/DownloadExecutor.js.map +0 -1
- package/dist/download/handlers/IllustrationTargetHandler.d.ts.map +0 -1
- package/dist/download/handlers/IllustrationTargetHandler.js.map +0 -1
- package/dist/download/handlers/NovelTargetHandler.d.ts.map +0 -1
- package/dist/download/handlers/NovelTargetHandler.js.map +0 -1
- package/dist/download/normalization/FileNameNormalizer.d.ts.map +0 -1
- package/dist/download/normalization/FileNameNormalizer.js.map +0 -1
- package/dist/download/normalization/FileOperationUtils.d.ts.map +0 -1
- package/dist/download/normalization/FileOperationUtils.js.map +0 -1
- package/dist/download/normalization/MetadataLoader.d.ts.map +0 -1
- package/dist/download/normalization/MetadataLoader.js.map +0 -1
- package/dist/download/pipeline/DownloadPipeline.d.ts.map +0 -1
- package/dist/download/pipeline/DownloadPipeline.js.map +0 -1
- package/dist/download/plan/DownloadPlanner.d.ts.map +0 -1
- package/dist/download/plan/DownloadPlanner.js.map +0 -1
- package/dist/download/recovery/ErrorRecovery.d.ts.map +0 -1
- package/dist/download/recovery/ErrorRecovery.js.map +0 -1
- package/dist/download/report/ProgressReporter.d.ts.map +0 -1
- package/dist/download/report/ProgressReporter.js.map +0 -1
- package/dist/download-specific.d.ts.map +0 -1
- package/dist/download-specific.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/interfaces/IDatabase.d.ts.map +0 -1
- package/dist/interfaces/IDatabase.js.map +0 -1
- package/dist/interfaces/IDownloadManager.d.ts.map +0 -1
- package/dist/interfaces/IDownloadManager.js.map +0 -1
- package/dist/interfaces/IFileService.d.ts.map +0 -1
- package/dist/interfaces/IFileService.js.map +0 -1
- package/dist/interfaces/IPixivAuth.d.ts.map +0 -1
- package/dist/interfaces/IPixivAuth.js.map +0 -1
- package/dist/interfaces/IPixivClient.d.ts.map +0 -1
- package/dist/interfaces/IPixivClient.js.map +0 -1
- package/dist/interfaces/index.d.ts.map +0 -1
- package/dist/interfaces/index.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/notification/NotificationPolicy.d.ts.map +0 -1
- package/dist/notification/NotificationPolicy.js.map +0 -1
- package/dist/pixiv/AuthClient.d.ts.map +0 -1
- package/dist/pixiv/AuthClient.js.map +0 -1
- package/dist/pixiv/PixivClient.d.ts.map +0 -1
- package/dist/pixiv/PixivClient.js.map +0 -1
- package/dist/pixiv-client/PixivAuthTokenProvider.d.ts.map +0 -1
- package/dist/pixiv-client/PixivAuthTokenProvider.js.map +0 -1
- package/dist/pixiv-client/PixivFlowPixivClient.d.ts.map +0 -1
- package/dist/pixiv-client/PixivFlowPixivClient.js.map +0 -1
- package/dist/pixiv-client/TargetSearchRunner.d.ts.map +0 -1
- package/dist/pixiv-client/TargetSearchRunner.js.map +0 -1
- package/dist/pixiv-client/createPixivFlowClient.d.ts.map +0 -1
- package/dist/pixiv-client/createPixivFlowClient.js.map +0 -1
- package/dist/pixiv-client/query-mapper.d.ts.map +0 -1
- package/dist/pixiv-client/query-mapper.js.map +0 -1
- package/dist/pixiv-token-getter-adapter.d.ts.map +0 -1
- package/dist/pixiv-token-getter-adapter.js.map +0 -1
- package/dist/puppeteer-login-adapter/auth-code.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/auth-code.js.map +0 -1
- package/dist/puppeteer-login-adapter/browser-launch.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/browser-launch.js.map +0 -1
- package/dist/puppeteer-login-adapter/constants.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/constants.js.map +0 -1
- package/dist/puppeteer-login-adapter/environment.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/environment.js.map +0 -1
- package/dist/puppeteer-login-adapter/login-headless.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/login-headless.js.map +0 -1
- package/dist/puppeteer-login-adapter/login-interactive.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/login-interactive.js.map +0 -1
- package/dist/puppeteer-login-adapter/pkce.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/pkce.js.map +0 -1
- package/dist/puppeteer-login-adapter/proxy.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/proxy.js.map +0 -1
- package/dist/puppeteer-login-adapter/token-exchange.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter/token-exchange.js.map +0 -1
- package/dist/puppeteer-login-adapter.d.ts.map +0 -1
- package/dist/puppeteer-login-adapter.js.map +0 -1
- package/dist/python-login-adapter/login-scripts.d.ts.map +0 -1
- package/dist/python-login-adapter/login-scripts.js.map +0 -1
- package/dist/python-login-adapter/proxy.d.ts.map +0 -1
- package/dist/python-login-adapter/proxy.js.map +0 -1
- package/dist/python-login-adapter/python-environment.d.ts.map +0 -1
- package/dist/python-login-adapter/python-environment.js.map +0 -1
- package/dist/python-login-adapter/response-converter.d.ts.map +0 -1
- package/dist/python-login-adapter/response-converter.js.map +0 -1
- package/dist/python-login-adapter.d.ts.map +0 -1
- package/dist/python-login-adapter.js.map +0 -1
- package/dist/random-download.d.ts.map +0 -1
- package/dist/random-download.js.map +0 -1
- package/dist/scheduler/MultiScheduleManager.d.ts.map +0 -1
- package/dist/scheduler/MultiScheduleManager.js.map +0 -1
- package/dist/scheduler/OccurrenceResolver.d.ts.map +0 -1
- package/dist/scheduler/OccurrenceResolver.js.map +0 -1
- package/dist/scheduler/ScheduleTriggerServer.d.ts.map +0 -1
- package/dist/scheduler/ScheduleTriggerServer.js.map +0 -1
- package/dist/scheduler/Scheduler.d.ts.map +0 -1
- package/dist/scheduler/Scheduler.js.map +0 -1
- package/dist/scheduler/SlotCoordinator.d.ts.map +0 -1
- package/dist/scheduler/SlotCoordinator.js.map +0 -1
- package/dist/scheduler/SlotStateMachine.d.ts.map +0 -1
- package/dist/scheduler/SlotStateMachine.js.map +0 -1
- package/dist/scheduler/TargetOutcome.d.ts.map +0 -1
- package/dist/scheduler/TargetOutcome.js.map +0 -1
- package/dist/scheduler/schedules.d.ts.map +0 -1
- package/dist/scheduler/schedules.js.map +0 -1
- package/dist/setup-wizard.d.ts.map +0 -1
- package/dist/setup-wizard.js.map +0 -1
- package/dist/storage/Database.d.ts.map +0 -1
- package/dist/storage/Database.js.map +0 -1
- package/dist/storage/DatabaseMigration.d.ts.map +0 -1
- package/dist/storage/DatabaseMigration.js.map +0 -1
- package/dist/storage/drivers/NodeSqliteDriver.d.ts.map +0 -1
- package/dist/storage/drivers/NodeSqliteDriver.js.map +0 -1
- package/dist/storage/drivers/SqliteDriver.d.ts.map +0 -1
- package/dist/storage/drivers/SqliteDriver.js.map +0 -1
- package/dist/storage/repositories/BaseRepository.d.ts.map +0 -1
- package/dist/storage/repositories/BaseRepository.js.map +0 -1
- package/dist/storage/repositories/ConfigHistoryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/ConfigHistoryRepository.js.map +0 -1
- package/dist/storage/repositories/DeliveryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DeliveryRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadQueryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadQueryRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadStatsRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadStatsRepository.js.map +0 -1
- package/dist/storage/repositories/DownloadWriteRepository.d.ts.map +0 -1
- package/dist/storage/repositories/DownloadWriteRepository.js.map +0 -1
- package/dist/storage/repositories/ExecutionRepository.d.ts.map +0 -1
- package/dist/storage/repositories/ExecutionRepository.js.map +0 -1
- package/dist/storage/repositories/MetadataRepository.d.ts.map +0 -1
- package/dist/storage/repositories/MetadataRepository.js.map +0 -1
- package/dist/storage/repositories/OutboxRepository.d.ts.map +0 -1
- package/dist/storage/repositories/OutboxRepository.js.map +0 -1
- package/dist/storage/repositories/RateLimitStateRepository.d.ts.map +0 -1
- package/dist/storage/repositories/RateLimitStateRepository.js.map +0 -1
- package/dist/storage/repositories/SchedulerRepository.d.ts.map +0 -1
- package/dist/storage/repositories/SchedulerRepository.js.map +0 -1
- package/dist/storage/repositories/SlotRepository.d.ts.map +0 -1
- package/dist/storage/repositories/SlotRepository.js.map +0 -1
- package/dist/storage/repositories/TaskHistoryRepository.d.ts.map +0 -1
- package/dist/storage/repositories/TaskHistoryRepository.js.map +0 -1
- package/dist/storage/repositories/TokenRepository.d.ts.map +0 -1
- package/dist/storage/repositories/TokenRepository.js.map +0 -1
- package/dist/tags/TagDiscoveryService.d.ts.map +0 -1
- package/dist/tags/TagDiscoveryService.js.map +0 -1
- package/dist/tags/TagDiscoveryStore.d.ts.map +0 -1
- package/dist/tags/TagDiscoveryStore.js.map +0 -1
- package/dist/tags/TagPlanApplier.d.ts.map +0 -1
- package/dist/tags/TagPlanApplier.js.map +0 -1
- package/dist/tags/types.d.ts.map +0 -1
- package/dist/tags/types.js.map +0 -1
- package/dist/terminal-login/adapter-selector.d.ts.map +0 -1
- package/dist/terminal-login/adapter-selector.js.map +0 -1
- package/dist/terminal-login/constants.d.ts.map +0 -1
- package/dist/terminal-login/constants.js.map +0 -1
- package/dist/terminal-login/credentials.d.ts.map +0 -1
- package/dist/terminal-login/credentials.js.map +0 -1
- package/dist/terminal-login/output.d.ts.map +0 -1
- package/dist/terminal-login/output.js.map +0 -1
- package/dist/terminal-login/pixiv-terminal-auth.d.ts.map +0 -1
- package/dist/terminal-login/pixiv-terminal-auth.js.map +0 -1
- package/dist/terminal-login/terminal-login.d.ts.map +0 -1
- package/dist/terminal-login/terminal-login.js.map +0 -1
- package/dist/terminal-login/token-refresh.d.ts.map +0 -1
- package/dist/terminal-login/token-refresh.js.map +0 -1
- package/dist/terminal-login/types.d.ts.map +0 -1
- package/dist/terminal-login/types.js.map +0 -1
- package/dist/terminal-login.d.ts.map +0 -1
- package/dist/terminal-login.js.map +0 -1
- package/dist/topic/TopicCache.d.ts.map +0 -1
- package/dist/topic/TopicCache.js.map +0 -1
- package/dist/topic/TopicPipeline.d.ts.map +0 -1
- package/dist/topic/TopicPipeline.js.map +0 -1
- package/dist/topic/TopicResolver.d.ts.map +0 -1
- package/dist/topic/TopicResolver.js.map +0 -1
- package/dist/topic/TopicTagScorer.d.ts.map +0 -1
- package/dist/topic/TopicTagScorer.js.map +0 -1
- package/dist/topic/createTopicPipeline.d.ts.map +0 -1
- package/dist/topic/createTopicPipeline.js.map +0 -1
- package/dist/topic/types.d.ts.map +0 -1
- package/dist/topic/types.js.map +0 -1
- package/dist/utils/ai-detection.d.ts.map +0 -1
- package/dist/utils/ai-detection.js.map +0 -1
- package/dist/utils/concurrency.d.ts.map +0 -1
- package/dist/utils/concurrency.js.map +0 -1
- package/dist/utils/config-manager/config-normalization.d.ts.map +0 -1
- package/dist/utils/config-manager/config-normalization.js.map +0 -1
- package/dist/utils/config-manager/config-persistence.d.ts.map +0 -1
- package/dist/utils/config-manager/config-persistence.js.map +0 -1
- package/dist/utils/config-manager/config-repair.d.ts.map +0 -1
- package/dist/utils/config-manager/config-repair.js.map +0 -1
- package/dist/utils/config-manager/file-discovery.d.ts.map +0 -1
- package/dist/utils/config-manager/file-discovery.js.map +0 -1
- package/dist/utils/config-manager/file-operations.d.ts.map +0 -1
- package/dist/utils/config-manager/file-operations.js.map +0 -1
- package/dist/utils/config-manager.d.ts.map +0 -1
- package/dist/utils/config-manager.js.map +0 -1
- package/dist/utils/config-parser/field-definitions.d.ts.map +0 -1
- package/dist/utils/config-parser/field-definitions.js.map +0 -1
- package/dist/utils/config-parser/parser.d.ts.map +0 -1
- package/dist/utils/config-parser/parser.js.map +0 -1
- package/dist/utils/config-parser/reporter.d.ts.map +0 -1
- package/dist/utils/config-parser/reporter.js.map +0 -1
- package/dist/utils/config-parser/validator.d.ts.map +0 -1
- package/dist/utils/config-parser/validator.js.map +0 -1
- package/dist/utils/config-parser.d.ts.map +0 -1
- package/dist/utils/config-parser.js.map +0 -1
- package/dist/utils/config-path-migrator/auto-fix.d.ts.map +0 -1
- package/dist/utils/config-path-migrator/auto-fix.js.map +0 -1
- package/dist/utils/config-path-migrator/path-migration-core.d.ts.map +0 -1
- package/dist/utils/config-path-migrator/path-migration-core.js.map +0 -1
- package/dist/utils/config-path-migrator/storage-path-migration.d.ts.map +0 -1
- package/dist/utils/config-path-migrator/storage-path-migration.js.map +0 -1
- package/dist/utils/config-path-migrator.d.ts.map +0 -1
- package/dist/utils/config-path-migrator.js.map +0 -1
- package/dist/utils/config-validator-unified.d.ts.map +0 -1
- package/dist/utils/config-validator-unified.js.map +0 -1
- package/dist/utils/date-utils.d.ts.map +0 -1
- package/dist/utils/date-utils.js.map +0 -1
- package/dist/utils/directory-info.d.ts.map +0 -1
- package/dist/utils/directory-info.js.map +0 -1
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/fs.d.ts.map +0 -1
- package/dist/utils/fs.js.map +0 -1
- package/dist/utils/language-detection.d.ts.map +0 -1
- package/dist/utils/language-detection.js.map +0 -1
- package/dist/utils/login-helper.d.ts.map +0 -1
- package/dist/utils/login-helper.js.map +0 -1
- package/dist/utils/pixiv-date-utils.d.ts.map +0 -1
- package/dist/utils/pixiv-date-utils.js.map +0 -1
- package/dist/utils/pixiv-sort.d.ts.map +0 -1
- package/dist/utils/pixiv-sort.js.map +0 -1
- package/dist/utils/pixiv-url-parser.d.ts.map +0 -1
- package/dist/utils/pixiv-url-parser.js.map +0 -1
- package/dist/utils/pixiv-utils.d.ts.map +0 -1
- package/dist/utils/pixiv-utils.js.map +0 -1
- package/dist/utils/project-root.d.ts.map +0 -1
- package/dist/utils/project-root.js.map +0 -1
- package/dist/utils/redact.d.ts.map +0 -1
- package/dist/utils/redact.js.map +0 -1
- package/dist/utils/resource-manager.d.ts.map +0 -1
- package/dist/utils/resource-manager.js.map +0 -1
- package/dist/utils/stdin.d.ts.map +0 -1
- package/dist/utils/stdin.js.map +0 -1
- package/dist/utils/target-label.d.ts.map +0 -1
- package/dist/utils/target-label.js.map +0 -1
- package/dist/utils/token-maintenance.d.ts.map +0 -1
- package/dist/utils/token-maintenance.js.map +0 -1
- package/dist/utils/token-manager.d.ts.map +0 -1
- package/dist/utils/token-manager.js.map +0 -1
- package/dist/utils/token-revocation.d.ts.map +0 -1
- package/dist/utils/token-revocation.js.map +0 -1
- package/dist/utils/type-guards.d.ts.map +0 -1
- package/dist/utils/type-guards.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/webui/index.d.ts.map +0 -1
- package/dist/webui/index.js.map +0 -1
- package/dist/webui/ports.d.ts.map +0 -1
- package/dist/webui/ports.js.map +0 -1
- package/dist/webui/routes/auth.d.ts.map +0 -1
- package/dist/webui/routes/auth.js.map +0 -1
- package/dist/webui/routes/config-utils.d.ts.map +0 -1
- package/dist/webui/routes/config-utils.js.map +0 -1
- package/dist/webui/routes/config.d.ts.map +0 -1
- package/dist/webui/routes/config.js.map +0 -1
- package/dist/webui/routes/download.d.ts.map +0 -1
- package/dist/webui/routes/download.js.map +0 -1
- package/dist/webui/routes/files.d.ts.map +0 -1
- package/dist/webui/routes/files.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-login-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-login-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-logout-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-logout-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-status-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-status-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-token-handler.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-token-handler.js.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-utils.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth/auth-utils.js.map +0 -1
- package/dist/webui/routes/handlers/auth-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/auth-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-backup-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-backup-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-basic-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-basic-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-diagnostic-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-diagnostic-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-files-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-files-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/config-history-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/config-history-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-basic-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-basic-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-batch-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-batch-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-incomplete-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-incomplete-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/download-url-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/download-url-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/files-list-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/files-list-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/files-operations-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/files-operations-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/logs-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/logs-handlers.js.map +0 -1
- package/dist/webui/routes/handlers/stats-handlers.d.ts.map +0 -1
- package/dist/webui/routes/handlers/stats-handlers.js.map +0 -1
- package/dist/webui/routes/logs.d.ts.map +0 -1
- package/dist/webui/routes/logs.js.map +0 -1
- package/dist/webui/routes/stats.d.ts.map +0 -1
- package/dist/webui/routes/stats.js.map +0 -1
- package/dist/webui/server/auth-middleware.d.ts.map +0 -1
- package/dist/webui/server/auth-middleware.js.map +0 -1
- package/dist/webui/server/server-middleware.d.ts.map +0 -1
- package/dist/webui/server/server-middleware.js.map +0 -1
- package/dist/webui/server/server-routes.d.ts.map +0 -1
- package/dist/webui/server/server-routes.js.map +0 -1
- package/dist/webui/server/server-static.d.ts.map +0 -1
- package/dist/webui/server/server-static.js.map +0 -1
- package/dist/webui/server/server-utils.d.ts.map +0 -1
- package/dist/webui/server/server-utils.js.map +0 -1
- package/dist/webui/server/server.d.ts.map +0 -1
- package/dist/webui/server/server.js.map +0 -1
- package/dist/webui/server/types.d.ts.map +0 -1
- package/dist/webui/server/types.js.map +0 -1
- package/dist/webui/services/DownloadTaskManager.d.ts.map +0 -1
- package/dist/webui/services/DownloadTaskManager.js.map +0 -1
- package/dist/webui/utils/config-validator.d.ts.map +0 -1
- package/dist/webui/utils/config-validator.js.map +0 -1
- package/dist/webui/utils/error-codes.d.ts.map +0 -1
- package/dist/webui/utils/error-codes.js.map +0 -1
- package/dist/webui/utils/task-serialize.d.ts.map +0 -1
- package/dist/webui/utils/task-serialize.js.map +0 -1
- package/dist/webui/websocket/DownloadStatus.d.ts.map +0 -1
- package/dist/webui/websocket/DownloadStatus.js.map +0 -1
- package/dist/webui/websocket/LogStream.d.ts.map +0 -1
- package/dist/webui/websocket/LogStream.js.map +0 -1
|
@@ -10,7 +10,9 @@ const config_1 = require("../config");
|
|
|
10
10
|
const MultiScheduleManager_1 = require("../scheduler/MultiScheduleManager");
|
|
11
11
|
const ScheduleTriggerServer_1 = require("../scheduler/ScheduleTriggerServer");
|
|
12
12
|
const SlotCoordinator_1 = require("../scheduler/SlotCoordinator");
|
|
13
|
+
const schedules_1 = require("../scheduler/schedules");
|
|
13
14
|
const scheduler_runtime_1 = require("./scheduler-runtime");
|
|
15
|
+
const SchedulerIdleLifecycle_1 = require("./SchedulerIdleLifecycle");
|
|
14
16
|
/**
|
|
15
17
|
* Decide whether the authenticated HTTP trigger server should be mounted. It is
|
|
16
18
|
* always up in `external` mode (the external clock needs it); in `internal`
|
|
@@ -51,6 +53,7 @@ class SchedulerCommand extends Command_1.BaseCommand {
|
|
|
51
53
|
execute: runtime.runJob,
|
|
52
54
|
database: runtime.database,
|
|
53
55
|
onFailure: runtime.notifyScheduleFailure,
|
|
56
|
+
onAbandoned: (_config, _schedule, abandoned) => runtime.abandonActiveRun(abandoned.errorMessage ?? 'abandoned after timeout'),
|
|
54
57
|
telemetry: {
|
|
55
58
|
beginRun: () => runtime.database.getOverviewStats().totalDownloads,
|
|
56
59
|
endRun: () => runtime.database.getOverviewStats().totalDownloads,
|
|
@@ -65,19 +68,24 @@ class SchedulerCommand extends Command_1.BaseCommand {
|
|
|
65
68
|
// Authenticated HTTP trigger. Mounted in external mode (the external clock
|
|
66
69
|
// wakes the machine here) and, opt-in, in internal mode for manual/ops
|
|
67
70
|
// triggers. Business logic lives in runJob/SlotCoordinator; this handler
|
|
68
|
-
// only authenticates,
|
|
69
|
-
//
|
|
71
|
+
// only authenticates, records the occurrence durably and hands it to the
|
|
72
|
+
// shared scheduler, then answers. It deliberately does not await the run:
|
|
73
|
+
// the open request is NOT an activity lease, and a 10-40 minute download
|
|
74
|
+
// would outlive the clock/proxy/client timeout carrying it.
|
|
70
75
|
let triggerServer = null;
|
|
71
76
|
if (triggerEnabled(runtime.config)) {
|
|
72
77
|
const rt = runtime.config.schedulerRuntime;
|
|
73
78
|
const coordinator = new SlotCoordinator_1.SlotCoordinator(runtime.database);
|
|
74
79
|
const resolveConfig = () => manager['activeConfig'] ?? runtime.config;
|
|
75
80
|
const findPlan = (cfg, scheduleId) => cfg.schedules?.find((s) => s.id === scheduleId && s.enabled !== false);
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
+
// Durable dispatch. The trigger endpoint records the occurrence FIRST,
|
|
82
|
+
// then hands it to the shared scheduler and answers immediately. It must
|
|
83
|
+
// never await the download itself: a 10-40 minute run outlives the
|
|
84
|
+
// router/proxy/clock timeouts, and a request that dies mid-run used to
|
|
85
|
+
// take the occurrence with it. Idempotency comes from the slot ledger and
|
|
86
|
+
// the cross-process lease, so concurrent clocks (Cloudflare + watchdog +
|
|
87
|
+
// manual) converge on one worker instead of needing an in-process
|
|
88
|
+
// singleflight map.
|
|
81
89
|
triggerServer = new ScheduleTriggerServer_1.ScheduleTriggerServer(ScheduleTriggerServer_1.ScheduleTriggerServer.resolveToken(rt.trigger?.token), {
|
|
82
90
|
listSchedules: () => manager.scheduleIds(),
|
|
83
91
|
resolve: (scheduleId, source, at, label) => {
|
|
@@ -90,41 +98,61 @@ class SchedulerCommand extends Command_1.BaseCommand {
|
|
|
90
98
|
return { error: resolved.error ?? 'could not resolve occurrence', status: resolved.status ?? 400 };
|
|
91
99
|
return { context: resolved.context };
|
|
92
100
|
},
|
|
93
|
-
run: (scheduleId, context) => {
|
|
94
|
-
const key = context.slotId;
|
|
95
|
-
const existing = inFlight.get(key);
|
|
96
|
-
if (existing)
|
|
97
|
-
return existing;
|
|
101
|
+
run: async (scheduleId, context) => {
|
|
98
102
|
const cfg = resolveConfig();
|
|
99
103
|
const plan = findPlan(cfg, scheduleId);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
104
|
+
if (!plan) {
|
|
105
|
+
return { scheduleId, slotId: context.slotId, disposition: 'rejected', status: 'failed' };
|
|
106
|
+
}
|
|
107
|
+
const targets = (0, schedules_1.selectScheduleTargets)(cfg.targets, plan);
|
|
108
|
+
if (targets.length === 0) {
|
|
109
|
+
return { scheduleId, slotId: context.slotId, disposition: 'rejected', status: 'pending' };
|
|
110
|
+
}
|
|
111
|
+
// Step 1: make the occurrence durable BEFORE answering. A crash
|
|
112
|
+
// between here and the claim leaves a `pending`, lease-less row that
|
|
113
|
+
// the recovery loop re-dispatches, so an accepted trigger can never
|
|
114
|
+
// be silently dropped.
|
|
115
|
+
const prepared = coordinator.prepare(context, plan, targets);
|
|
116
|
+
if (prepared.alreadyCompleted) {
|
|
117
|
+
return {
|
|
118
|
+
...coordinator.completedSummary(context.slotId, plan),
|
|
119
|
+
disposition: 'already_completed',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Step 2: admit to the scheduler. Fire-and-forget by contract — the
|
|
123
|
+
// boolean only reports whether this process took the work.
|
|
124
|
+
const admitted = manager.triggerSchedule(scheduleId, {
|
|
125
|
+
triggerSource: 'http',
|
|
126
|
+
slot: context,
|
|
127
|
+
});
|
|
128
|
+
const rec = runtime.database.slots.getSlot(context.slotId);
|
|
129
|
+
if (admitted) {
|
|
130
|
+
return {
|
|
131
|
+
scheduleId,
|
|
132
|
+
slotId: context.slotId,
|
|
133
|
+
disposition: 'accepted',
|
|
134
|
+
status: rec?.status ?? 'pending',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// Not admitted locally. A live lease means another worker already
|
|
138
|
+
// owns the occurrence; anything else is a refusal this process could
|
|
139
|
+
// not take (busy/stopped/limit), which the clock may retry and the
|
|
140
|
+
// recovery loop will also pick up.
|
|
141
|
+
const lease = runtime.database.slots.getSlotLease(context.slotId);
|
|
142
|
+
if (lease.owner && lease.until && lease.until > Date.now()) {
|
|
143
|
+
return {
|
|
144
|
+
scheduleId,
|
|
145
|
+
slotId: context.slotId,
|
|
146
|
+
disposition: 'already_running',
|
|
147
|
+
status: 'running',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
scheduleId,
|
|
152
|
+
slotId: context.slotId,
|
|
153
|
+
disposition: 'rejected',
|
|
154
|
+
status: rec?.status ?? 'pending',
|
|
155
|
+
};
|
|
128
156
|
},
|
|
129
157
|
drainOutbox: () => runtime.drainOutbox(),
|
|
130
158
|
status: (scheduleId) => {
|
|
@@ -145,15 +173,47 @@ class SchedulerCommand extends Command_1.BaseCommand {
|
|
|
145
173
|
? 'External scheduler mode: internal cron disabled, awaiting authenticated schedule triggers'
|
|
146
174
|
: 'Internal scheduler mode with authenticated HTTP trigger enabled', { port });
|
|
147
175
|
}
|
|
148
|
-
|
|
149
|
-
|
|
176
|
+
// Run-to-completion lifecycle for the external clock. The machine was woken
|
|
177
|
+
// by a trigger and has to return to `stopped` by itself. Platform-side
|
|
178
|
+
// auto-stop cannot decide that: the trigger answers long before the run
|
|
179
|
+
// finishes, so the proxy sees an idle socket while downloads are still in
|
|
180
|
+
// flight. The worker therefore reads its OWN durable ledger (see
|
|
181
|
+
// SchedulerIdleLifecycle). Wired after the trigger server so the port is
|
|
182
|
+
// already being served when the first idle probe runs.
|
|
183
|
+
let idleLifecycle = null;
|
|
184
|
+
const shutdown = (reason, code = 0) => {
|
|
185
|
+
context.logger.info('Shutting down PixivFlow', { reason });
|
|
186
|
+
idleLifecycle?.stop();
|
|
150
187
|
triggerServer?.stop();
|
|
151
188
|
manager.stop();
|
|
152
189
|
runtime.close();
|
|
153
|
-
process.exit(
|
|
190
|
+
process.exit(code);
|
|
154
191
|
};
|
|
155
|
-
|
|
156
|
-
|
|
192
|
+
const scheduling = runtime.config.schedulerRuntime;
|
|
193
|
+
if (scheduling?.mode === 'external' && scheduling.exitWhenIdle) {
|
|
194
|
+
idleLifecycle = new SchedulerIdleLifecycle_1.SchedulerIdleLifecycle({
|
|
195
|
+
snapshot: () => {
|
|
196
|
+
const outbox = runtime.database.outbox.counts();
|
|
197
|
+
return {
|
|
198
|
+
activeSlots: runtime.database.slots.countActiveSlots(),
|
|
199
|
+
processingOutbox: outbox.processing,
|
|
200
|
+
// `pending` covers pending + retry_wait: a delivery still backing
|
|
201
|
+
// off is active work. Backoff is bounded (rows flip to `dead` once
|
|
202
|
+
// attempts are exhausted, which does not block exit), so this
|
|
203
|
+
// cannot hold the machine open indefinitely.
|
|
204
|
+
pendingOutbox: outbox.pending,
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
idleGraceMs: scheduling.idleGraceMs,
|
|
208
|
+
maxLifetimeMs: scheduling.maxLifetimeMs,
|
|
209
|
+
onExit: (reason) => shutdown(reason === 'idle'
|
|
210
|
+
? 'external worker idle: slot ledger and outbox are both empty'
|
|
211
|
+
: 'external worker exceeded the maxLifetimeMs backstop'),
|
|
212
|
+
});
|
|
213
|
+
idleLifecycle.start();
|
|
214
|
+
}
|
|
215
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
216
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
157
217
|
process.on('SIGHUP', () => {
|
|
158
218
|
context.logger.info('Received SIGHUP; reloading scheduler configuration');
|
|
159
219
|
manager.reload();
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run-to-completion lifecycle for the external-clock (autosleep) deployment.
|
|
3
|
+
*
|
|
4
|
+
* A PixivFlow machine can be woken by an authenticated HTTP trigger, execute a
|
|
5
|
+
* schedule, deliver through the durable outbox, and must then return to
|
|
6
|
+
* `stopped` on its own. Two things make that non-trivial:
|
|
7
|
+
*
|
|
8
|
+
* - Platform-side auto-stop cannot be used. The trigger endpoint answers as
|
|
9
|
+
* soon as the occurrence is durable (deliberately — a 10-40 minute run cannot
|
|
10
|
+
* survive a router/proxy/client timeout), so from the proxy's point of view
|
|
11
|
+
* the machine goes idle while the run is still in progress. Auto-stop would
|
|
12
|
+
* kill a download mid-flight.
|
|
13
|
+
* - "The request returned" and "nobody connected recently" are both wrong
|
|
14
|
+
* signals. HTTP activity says nothing about whether a download is still
|
|
15
|
+
* running, a Slot is still open, or an outbox row is still retrying.
|
|
16
|
+
*
|
|
17
|
+
* So this reads the worker's OWN durable ledger instead, and exits only when
|
|
18
|
+
* that ledger says nothing is left: no non-terminal Slot, no in-flight outbox
|
|
19
|
+
* row, no undelivered outbox row. It is deliberately the only thing it does —
|
|
20
|
+
* no second business state machine, no scheduler of its own, no queue.
|
|
21
|
+
*
|
|
22
|
+
* The idle grace is a merge window, not a timeout: two schedules ten minutes
|
|
23
|
+
* apart are normally served by a single wake-up, and a delivery retry that lands
|
|
24
|
+
* just after the run is drained without paying for a second cold start.
|
|
25
|
+
*
|
|
26
|
+
* `maxLifetimeMs` is a backstop, never the normal path. It exists so a worker
|
|
27
|
+
* that somehow never reaches idle (a wedged run, a Slot that recovery cannot
|
|
28
|
+
* re-dispatch) cannot bill indefinitely. Reaching it deletes nothing: the
|
|
29
|
+
* process closes its database cleanly and the next wake resumes from the same
|
|
30
|
+
* Slot/outbox rows.
|
|
31
|
+
*/
|
|
32
|
+
export type IdleExitReason = 'idle' | 'max-lifetime';
|
|
33
|
+
/**
|
|
34
|
+
* A read-only view of the worker's own authoritative state. Every field must be
|
|
35
|
+
* derived from durable rows, never from in-memory scheduling or HTTP activity.
|
|
36
|
+
*/
|
|
37
|
+
export interface IdleSnapshot {
|
|
38
|
+
/** Non-terminal Slots (pending/running): work this ledger still owes. */
|
|
39
|
+
activeSlots: number;
|
|
40
|
+
/** Outbox rows currently claimed by this or another live worker. */
|
|
41
|
+
processingOutbox: number;
|
|
42
|
+
/** Undelivered outbox rows (pending, or waiting on a bounded retry backoff). */
|
|
43
|
+
pendingOutbox: number;
|
|
44
|
+
}
|
|
45
|
+
/** True only when the worker has no work of any kind left. */
|
|
46
|
+
export declare function isIdle(snapshot: IdleSnapshot): boolean;
|
|
47
|
+
/** Default idle grace: long enough to merge two schedules ~10 minutes apart. */
|
|
48
|
+
export declare const DEFAULT_IDLE_GRACE_MS: number;
|
|
49
|
+
/** Default backstop on awake time; the idle detector is the normal exit path. */
|
|
50
|
+
export declare const DEFAULT_MAX_LIFETIME_MS: number;
|
|
51
|
+
/** How often the durable ledger is re-read. */
|
|
52
|
+
export declare const DEFAULT_IDLE_POLL_MS: number;
|
|
53
|
+
export interface IdleLifecycleOptions {
|
|
54
|
+
/** Reads the worker's authoritative state. Must not mutate anything. */
|
|
55
|
+
snapshot(): IdleSnapshot;
|
|
56
|
+
/**
|
|
57
|
+
* Invoked at most once, either on real idleness or on the hard backstop. The
|
|
58
|
+
* caller owns the shutdown itself (stop serving, flush, exit).
|
|
59
|
+
*/
|
|
60
|
+
onExit(reason: IdleExitReason): void;
|
|
61
|
+
idleGraceMs?: number;
|
|
62
|
+
maxLifetimeMs?: number;
|
|
63
|
+
pollMs?: number;
|
|
64
|
+
}
|
|
65
|
+
export declare class SchedulerIdleLifecycle {
|
|
66
|
+
private readonly options;
|
|
67
|
+
private readonly idleGraceMs;
|
|
68
|
+
private readonly maxLifetimeMs;
|
|
69
|
+
private readonly pollMs;
|
|
70
|
+
private pollTimer;
|
|
71
|
+
private lifetimeTimer;
|
|
72
|
+
private idleSince;
|
|
73
|
+
private exiting;
|
|
74
|
+
constructor(options: IdleLifecycleOptions);
|
|
75
|
+
/** Arm polling and the backstop. Idempotent. */
|
|
76
|
+
start(): void;
|
|
77
|
+
/** Disarm without exiting. Idempotent; used by the signal shutdown path. */
|
|
78
|
+
stop(): void;
|
|
79
|
+
private poll;
|
|
80
|
+
private exit;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=SchedulerIdleLifecycle.d.ts.map
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Run-to-completion lifecycle for the external-clock (autosleep) deployment.
|
|
4
|
+
*
|
|
5
|
+
* A PixivFlow machine can be woken by an authenticated HTTP trigger, execute a
|
|
6
|
+
* schedule, deliver through the durable outbox, and must then return to
|
|
7
|
+
* `stopped` on its own. Two things make that non-trivial:
|
|
8
|
+
*
|
|
9
|
+
* - Platform-side auto-stop cannot be used. The trigger endpoint answers as
|
|
10
|
+
* soon as the occurrence is durable (deliberately — a 10-40 minute run cannot
|
|
11
|
+
* survive a router/proxy/client timeout), so from the proxy's point of view
|
|
12
|
+
* the machine goes idle while the run is still in progress. Auto-stop would
|
|
13
|
+
* kill a download mid-flight.
|
|
14
|
+
* - "The request returned" and "nobody connected recently" are both wrong
|
|
15
|
+
* signals. HTTP activity says nothing about whether a download is still
|
|
16
|
+
* running, a Slot is still open, or an outbox row is still retrying.
|
|
17
|
+
*
|
|
18
|
+
* So this reads the worker's OWN durable ledger instead, and exits only when
|
|
19
|
+
* that ledger says nothing is left: no non-terminal Slot, no in-flight outbox
|
|
20
|
+
* row, no undelivered outbox row. It is deliberately the only thing it does —
|
|
21
|
+
* no second business state machine, no scheduler of its own, no queue.
|
|
22
|
+
*
|
|
23
|
+
* The idle grace is a merge window, not a timeout: two schedules ten minutes
|
|
24
|
+
* apart are normally served by a single wake-up, and a delivery retry that lands
|
|
25
|
+
* just after the run is drained without paying for a second cold start.
|
|
26
|
+
*
|
|
27
|
+
* `maxLifetimeMs` is a backstop, never the normal path. It exists so a worker
|
|
28
|
+
* that somehow never reaches idle (a wedged run, a Slot that recovery cannot
|
|
29
|
+
* re-dispatch) cannot bill indefinitely. Reaching it deletes nothing: the
|
|
30
|
+
* process closes its database cleanly and the next wake resumes from the same
|
|
31
|
+
* Slot/outbox rows.
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.SchedulerIdleLifecycle = exports.DEFAULT_IDLE_POLL_MS = exports.DEFAULT_MAX_LIFETIME_MS = exports.DEFAULT_IDLE_GRACE_MS = void 0;
|
|
35
|
+
exports.isIdle = isIdle;
|
|
36
|
+
const logger_1 = require("../logger");
|
|
37
|
+
/** True only when the worker has no work of any kind left. */
|
|
38
|
+
function isIdle(snapshot) {
|
|
39
|
+
return (snapshot.activeSlots === 0 &&
|
|
40
|
+
snapshot.processingOutbox === 0 &&
|
|
41
|
+
snapshot.pendingOutbox === 0);
|
|
42
|
+
}
|
|
43
|
+
/** Default idle grace: long enough to merge two schedules ~10 minutes apart. */
|
|
44
|
+
exports.DEFAULT_IDLE_GRACE_MS = 10 * 60 * 1000;
|
|
45
|
+
/** Default backstop on awake time; the idle detector is the normal exit path. */
|
|
46
|
+
exports.DEFAULT_MAX_LIFETIME_MS = 3 * 60 * 60 * 1000;
|
|
47
|
+
/** How often the durable ledger is re-read. */
|
|
48
|
+
exports.DEFAULT_IDLE_POLL_MS = 15 * 1000;
|
|
49
|
+
class SchedulerIdleLifecycle {
|
|
50
|
+
options;
|
|
51
|
+
idleGraceMs;
|
|
52
|
+
maxLifetimeMs;
|
|
53
|
+
pollMs;
|
|
54
|
+
pollTimer = null;
|
|
55
|
+
lifetimeTimer = null;
|
|
56
|
+
idleSince = null;
|
|
57
|
+
exiting = false;
|
|
58
|
+
constructor(options) {
|
|
59
|
+
this.options = options;
|
|
60
|
+
this.idleGraceMs = options.idleGraceMs ?? exports.DEFAULT_IDLE_GRACE_MS;
|
|
61
|
+
this.maxLifetimeMs = options.maxLifetimeMs ?? exports.DEFAULT_MAX_LIFETIME_MS;
|
|
62
|
+
this.pollMs = options.pollMs ?? exports.DEFAULT_IDLE_POLL_MS;
|
|
63
|
+
}
|
|
64
|
+
/** Arm polling and the backstop. Idempotent. */
|
|
65
|
+
start() {
|
|
66
|
+
if (this.pollTimer || this.lifetimeTimer)
|
|
67
|
+
return;
|
|
68
|
+
logger_1.logger.info('External worker lifecycle armed; will exit once real idleness is confirmed', {
|
|
69
|
+
idleGraceMs: this.idleGraceMs,
|
|
70
|
+
maxLifetimeMs: this.maxLifetimeMs,
|
|
71
|
+
pollMs: this.pollMs,
|
|
72
|
+
});
|
|
73
|
+
this.lifetimeTimer = setTimeout(() => this.exit('max-lifetime'), this.maxLifetimeMs);
|
|
74
|
+
this.pollTimer = setInterval(() => this.poll(), this.pollMs);
|
|
75
|
+
}
|
|
76
|
+
/** Disarm without exiting. Idempotent; used by the signal shutdown path. */
|
|
77
|
+
stop() {
|
|
78
|
+
if (this.pollTimer) {
|
|
79
|
+
clearInterval(this.pollTimer);
|
|
80
|
+
this.pollTimer = null;
|
|
81
|
+
}
|
|
82
|
+
if (this.lifetimeTimer) {
|
|
83
|
+
clearTimeout(this.lifetimeTimer);
|
|
84
|
+
this.lifetimeTimer = null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
poll() {
|
|
88
|
+
if (this.exiting)
|
|
89
|
+
return;
|
|
90
|
+
let snapshot;
|
|
91
|
+
try {
|
|
92
|
+
snapshot = this.options.snapshot();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// A failed read is NOT evidence of idleness. Stay awake and retry; the
|
|
96
|
+
// backstop still bounds how long a broken probe can hold the machine.
|
|
97
|
+
logger_1.logger.warn('Idle probe failed; staying awake and will retry', {
|
|
98
|
+
error: error instanceof Error ? error.message : String(error),
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!isIdle(snapshot)) {
|
|
103
|
+
if (this.idleSince !== null) {
|
|
104
|
+
logger_1.logger.info('External worker picked up new work; idle grace window cancelled', { ...snapshot });
|
|
105
|
+
}
|
|
106
|
+
this.idleSince = null;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (this.idleGraceMs === 0) {
|
|
110
|
+
this.exit('idle');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
if (this.idleSince === null) {
|
|
115
|
+
this.idleSince = now;
|
|
116
|
+
logger_1.logger.info('External worker idle; starting exit grace window', {
|
|
117
|
+
idleGraceMs: this.idleGraceMs,
|
|
118
|
+
...snapshot,
|
|
119
|
+
});
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (now - this.idleSince >= this.idleGraceMs) {
|
|
123
|
+
this.exit('idle');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exit(reason) {
|
|
127
|
+
if (this.exiting)
|
|
128
|
+
return;
|
|
129
|
+
this.exiting = true;
|
|
130
|
+
this.stop();
|
|
131
|
+
this.options.onExit(reason);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.SchedulerIdleLifecycle = SchedulerIdleLifecycle;
|
|
135
|
+
//# sourceMappingURL=SchedulerIdleLifecycle.js.map
|
|
@@ -61,6 +61,12 @@ export interface SchedulerRuntime {
|
|
|
61
61
|
runJob(snapshot: StandaloneConfig, schedule: ScheduleConfig, options?: RunJobOptions): Promise<void>;
|
|
62
62
|
/** Cancel the in-flight download plan, if any. */
|
|
63
63
|
cancelActive(reason: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* The active run never settled after its timeout and drain window. Stop
|
|
66
|
+
* renewing its lease and take its Slot terminal so recovery cannot re-dispatch
|
|
67
|
+
* the same occurrence alongside the still-running job.
|
|
68
|
+
*/
|
|
69
|
+
abandonActiveRun(reason: string): void;
|
|
64
70
|
/** Notify the affected review group after a failed scheduled run. */
|
|
65
71
|
notifyScheduleFailure(snapshot: StandaloneConfig, schedule: ScheduleConfig, failure: JobFailure): Promise<void>;
|
|
66
72
|
/** Start the independent outbox pump (long-running daemon). */
|
|
@@ -203,6 +203,12 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
203
203
|
tokenMaintenance.start();
|
|
204
204
|
}
|
|
205
205
|
let activeDownloadManager = null;
|
|
206
|
+
/**
|
|
207
|
+
* Lease bookkeeping of the run that currently owns a Slot, exposed to
|
|
208
|
+
* cancelActive()/abandonActiveRun() (defined below, outside runJob's closure).
|
|
209
|
+
* Set while a lease is held and cleared as soon as it is released.
|
|
210
|
+
*/
|
|
211
|
+
let activeLeaseHooks = null;
|
|
206
212
|
// Independently-pumped durable outbox (content + notifications). Started in
|
|
207
213
|
// the long-running scheduler daemon; run-once drains explicitly before exit.
|
|
208
214
|
const deliveryDispatcher = new DeliveryDispatcher_1.DeliveryDispatcher(config.delivery, buildProxyUrl(config.network));
|
|
@@ -252,6 +258,12 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
252
258
|
// resolve a canonical occurrence and converge on one durable Slot.
|
|
253
259
|
let slotCtx = null;
|
|
254
260
|
let varReleaseLease = null;
|
|
261
|
+
/**
|
|
262
|
+
* Set when the run was abandoned past its drain window. A wedged job can
|
|
263
|
+
* still return much later; it must not roll the slot up again (which would
|
|
264
|
+
* overwrite the terminal `failed` record the abandon path wrote).
|
|
265
|
+
*/
|
|
266
|
+
let slotAbandoned = false;
|
|
255
267
|
if (!adhoc) {
|
|
256
268
|
if (providedSlot) {
|
|
257
269
|
slotCtx = providedSlot;
|
|
@@ -271,19 +283,20 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
271
283
|
}
|
|
272
284
|
const activeSlot = slotCtx;
|
|
273
285
|
const runOwner = `run-${process.pid}-${(0, node_crypto_1.randomUUID)().slice(0, 8)}`;
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
if (begin.alreadyCompleted && !onlyTarget) {
|
|
286
|
+
const prepared = coordinator.prepare(slotCtx, schedule, targets);
|
|
287
|
+
if (prepared.alreadyCompleted && !onlyTarget) {
|
|
277
288
|
logger_1.logger.info('Slot already terminal; nothing to do', {
|
|
278
289
|
slot: activeSlot.slotId,
|
|
279
|
-
status:
|
|
290
|
+
status: prepared.slotRec.status,
|
|
280
291
|
});
|
|
281
292
|
return;
|
|
282
293
|
}
|
|
283
294
|
// Cross-process lease: a concurrent Cloudflare + watchdog + manual
|
|
284
295
|
// trigger on a SECOND process sees an active lease and converges instead
|
|
285
|
-
// of running the same targets in parallel.
|
|
286
|
-
|
|
296
|
+
// of running the same targets in parallel. The lease TTL bounds how long a
|
|
297
|
+
// DEAD worker blocks its slot; it is intentionally independent of
|
|
298
|
+
// schedule.timeout (which bounds how long a live run may take).
|
|
299
|
+
const claimed = coordinator.claimRunLease(activeSlot.slotId, runOwner, SlotCoordinator_1.SLOT_LEASE_TTL_MS);
|
|
287
300
|
if (!claimed) {
|
|
288
301
|
const lease = database.slots.getSlotLease(activeSlot.slotId);
|
|
289
302
|
logger_1.logger.info('Slot run already leased by another worker; converging', {
|
|
@@ -295,12 +308,48 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
295
308
|
await outboxWorker.drainOnce(1);
|
|
296
309
|
return;
|
|
297
310
|
}
|
|
298
|
-
|
|
311
|
+
// Only the process that actually owns the lease may report the slot as
|
|
312
|
+
// running; the accepting adapter records it as pending instead.
|
|
313
|
+
coordinator.markRunning(activeSlot.slotId);
|
|
314
|
+
let cancelled = false;
|
|
315
|
+
const heartbeat = setInterval(() => {
|
|
316
|
+
// A cancelled/timed-out run must stop renewing its lease. An infinitely
|
|
317
|
+
// renewed lease is precisely what made a wedged run unrecoverable: the
|
|
318
|
+
// heartbeat outlived the scheduler timeout, so no other worker could ever
|
|
319
|
+
// claim the slot and the occurrence stayed stuck in `running` forever.
|
|
320
|
+
if (cancelled) {
|
|
321
|
+
clearInterval(heartbeat);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
coordinator.heartbeatLease(activeSlot.slotId, runOwner, SlotCoordinator_1.SLOT_LEASE_TTL_MS);
|
|
325
|
+
}, SlotCoordinator_1.SLOT_HEARTBEAT_MS);
|
|
299
326
|
heartbeat.unref?.();
|
|
300
327
|
varReleaseLease = () => {
|
|
301
328
|
clearInterval(heartbeat);
|
|
329
|
+
activeLeaseHooks = null;
|
|
302
330
|
coordinator.releaseRunLease(activeSlot.slotId, runOwner);
|
|
303
331
|
};
|
|
332
|
+
activeLeaseHooks = {
|
|
333
|
+
stopHeartbeat: () => {
|
|
334
|
+
cancelled = true;
|
|
335
|
+
clearInterval(heartbeat);
|
|
336
|
+
},
|
|
337
|
+
/**
|
|
338
|
+
* The run will never settle: stop heart-beating AND leave the ledger
|
|
339
|
+
* terminal, so the recovery sweep (which only acts on `pending`/`running`
|
|
340
|
+
* slots with no live lease) cannot re-dispatch this occurrence next to a
|
|
341
|
+
* still-running job. Merely dropping the lease would be wrong here —
|
|
342
|
+
* unlike a crash, this worker is alive.
|
|
343
|
+
*/
|
|
344
|
+
abandon: (reason) => {
|
|
345
|
+
cancelled = true;
|
|
346
|
+
slotAbandoned = true;
|
|
347
|
+
clearInterval(heartbeat);
|
|
348
|
+
database.slots.markSlotStatus(activeSlot.slotId, 'failed', `abandoned after scheduler timeout; no delivery (${reason})`);
|
|
349
|
+
coordinator.releaseRunLease(activeSlot.slotId, runOwner);
|
|
350
|
+
activeLeaseHooks = null;
|
|
351
|
+
},
|
|
352
|
+
};
|
|
304
353
|
}
|
|
305
354
|
// For a scheduled run, skip cells already in a terminal state (resume never
|
|
306
355
|
// re-runs a finished cell — that is what prevents a second post). Membership
|
|
@@ -310,6 +359,10 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
310
359
|
if (slotCtx && runTargets.length === 0) {
|
|
311
360
|
logger_1.logger.info('All slot cells already complete', { slot: slotCtx.slotId });
|
|
312
361
|
coordinator.finish(slotCtx, schedule, targets);
|
|
362
|
+
// Release LAST: the slot must stay owned until its aggregate state has
|
|
363
|
+
// been rolled up, otherwise a concurrent trigger could claim and re-run it
|
|
364
|
+
// against a half-finished ledger.
|
|
365
|
+
varReleaseLease?.();
|
|
313
366
|
return;
|
|
314
367
|
}
|
|
315
368
|
// Ad-hoc refetch with a filter that matched no target (or all filtered out):
|
|
@@ -398,6 +451,10 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
398
451
|
}
|
|
399
452
|
catch (error) {
|
|
400
453
|
if (!slotCtx || !(error instanceof Error) || !/^All \d+ target\(s\) failed\./.test(error.message)) {
|
|
454
|
+
// Abnormal abort: no roll-up is possible, so hand the slot back now
|
|
455
|
+
// instead of holding it until the lease TTL expires. Recovery sees a
|
|
456
|
+
// non-terminal slot with no live lease and resumes the SAME occurrence.
|
|
457
|
+
releaseLease?.();
|
|
401
458
|
throw error;
|
|
402
459
|
}
|
|
403
460
|
// Scheduled Slots treat terminal target failures (failed/no_candidate) as
|
|
@@ -408,10 +465,9 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
408
465
|
finally {
|
|
409
466
|
if (activeDownloadManager === downloadManager)
|
|
410
467
|
activeDownloadManager = null;
|
|
411
|
-
releaseLease?.();
|
|
412
468
|
}
|
|
413
469
|
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
414
|
-
if (slotCtx) {
|
|
470
|
+
if (slotCtx && !slotAbandoned) {
|
|
415
471
|
const summary = coordinator.finish(slotCtx, schedule, targets);
|
|
416
472
|
notificationPolicy.sendSlotSummary(slotCtx, schedule, summary.cells.map((c) => {
|
|
417
473
|
const t = targets.find((x) => x.id === c.targetId);
|
|
@@ -437,6 +493,17 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
437
493
|
};
|
|
438
494
|
const cancelActive = (reason) => {
|
|
439
495
|
activeDownloadManager?.cancel(reason);
|
|
496
|
+
// Scheduler timeout / process shutdown also stop the lease heartbeat. The run
|
|
497
|
+
// is on its way out (or about to be killed with the process), so it must not
|
|
498
|
+
// keep the slot locked while it unwinds. A shutdown deliberately leaves the
|
|
499
|
+
// slot NON-terminal: recovery resumes the same occurrence after restart.
|
|
500
|
+
activeLeaseHooks?.stopHeartbeat();
|
|
501
|
+
};
|
|
502
|
+
const abandonActiveRun = (reason) => {
|
|
503
|
+
// Cancellation did not take effect inside the drain window — a request that
|
|
504
|
+
// ignores the abort. This run will never settle, so finish the lease
|
|
505
|
+
// bookkeeping it cannot do itself.
|
|
506
|
+
activeLeaseHooks?.abandon(reason);
|
|
440
507
|
};
|
|
441
508
|
const close = () => {
|
|
442
509
|
outboxWorker.stop();
|
|
@@ -454,6 +521,7 @@ async function createSchedulerRuntime(configPathArg) {
|
|
|
454
521
|
tokenMaintenance,
|
|
455
522
|
runJob,
|
|
456
523
|
cancelActive,
|
|
524
|
+
abandonActiveRun,
|
|
457
525
|
startOutboxWorker: () => outboxWorker.start(),
|
|
458
526
|
drainOutbox: () => outboxWorker.drainOnce(),
|
|
459
527
|
notifyScheduleFailure: (snapshot, schedule, failure) => notifyScheduleFailure(snapshot, database, schedule, failure),
|
package/dist/config/types.d.ts
CHANGED
|
@@ -426,6 +426,39 @@ export interface SchedulerRuntimeConfig {
|
|
|
426
426
|
* One pending run per plan is retained; extra ticks are skipped.
|
|
427
427
|
*/
|
|
428
428
|
queueLimit?: number;
|
|
429
|
+
/**
|
|
430
|
+
* External-mode run-to-completion lifecycle. When true, the daemon exits the
|
|
431
|
+
* process once its OWN durable ledger says there is nothing left to do, so a
|
|
432
|
+
* machine that was woken by an external clock returns to `stopped` by itself
|
|
433
|
+
* instead of idling on a fixed clock until the platform stops it.
|
|
434
|
+
*
|
|
435
|
+
* The platform's proxy-side auto-stop cannot make this call. The HTTP trigger
|
|
436
|
+
* answers as soon as the occurrence is durable (deliberately — a 10-40 minute
|
|
437
|
+
* run cannot survive a router/proxy/client timeout), so the proxy sees an idle
|
|
438
|
+
* socket while the real run is still going and would stop the machine
|
|
439
|
+
* mid-download. Only this process knows when its work is finished.
|
|
440
|
+
*
|
|
441
|
+
* Ignored in `internal` mode, where cron owns the process lifetime.
|
|
442
|
+
* Default: false.
|
|
443
|
+
*/
|
|
444
|
+
exitWhenIdle?: boolean;
|
|
445
|
+
/**
|
|
446
|
+
* How long to stay awake after the last observed work, before exiting in
|
|
447
|
+
* `exitWhenIdle` mode. This is a merge window rather than a timeout: two
|
|
448
|
+
* schedules ten minutes apart are normally served by ONE wake-up, and a
|
|
449
|
+
* delivery retry that lands shortly after the run is drained without paying
|
|
450
|
+
* for a second cold start. Default: 600000ms (10 minutes).
|
|
451
|
+
*/
|
|
452
|
+
idleGraceMs?: number;
|
|
453
|
+
/**
|
|
454
|
+
* Hard backstop on total awake time in `exitWhenIdle` mode. The native idle
|
|
455
|
+
* detector is the normal exit path and this should never fire; it exists so a
|
|
456
|
+
* worker that never reaches idle (a wedged run, a Slot recovery cannot
|
|
457
|
+
* re-dispatch) cannot bill indefinitely. On expiry the daemon logs the reason,
|
|
458
|
+
* closes its database cleanly and exits. Nothing is deleted — the next wake
|
|
459
|
+
* resumes from the same durable Slot/outbox rows. Default: 10800000ms (3h).
|
|
460
|
+
*/
|
|
461
|
+
maxLifetimeMs?: number;
|
|
429
462
|
/**
|
|
430
463
|
* External-mode HTTP trigger settings. Ignored in internal mode.
|
|
431
464
|
*/
|