@overlordai/server 1.0.53 → 1.0.55
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/database/migrations/001-init-schema.sql +73 -9
- package/dist/adapters/adapter.interface.d.ts +1 -4
- package/dist/adapters/adapter.interface.d.ts.map +1 -1
- package/dist/adapters/adapter.interface.js.map +1 -1
- package/dist/adapters/adapter.module.d.ts.map +1 -1
- package/dist/adapters/adapter.module.js +8 -7
- package/dist/adapters/adapter.module.js.map +1 -1
- package/dist/adapters/lark/lark-card.builder.d.ts +1 -25
- package/dist/adapters/lark/lark-card.builder.d.ts.map +1 -1
- package/dist/adapters/lark/lark-card.builder.js +6 -110
- package/dist/adapters/lark/lark-card.builder.js.map +1 -1
- package/dist/adapters/lark/lark-message.parser.d.ts +4 -4
- package/dist/adapters/lark/lark-message.parser.d.ts.map +1 -1
- package/dist/adapters/lark/lark-message.parser.js +20 -13
- package/dist/adapters/lark/lark-message.parser.js.map +1 -1
- package/dist/adapters/lark/lark-signature.d.ts.map +1 -1
- package/dist/adapters/lark/lark-signature.js +6 -0
- package/dist/adapters/lark/lark-signature.js.map +1 -1
- package/dist/adapters/lark/lark.adapter.d.ts +14 -9
- package/dist/adapters/lark/lark.adapter.d.ts.map +1 -1
- package/dist/adapters/lark/lark.adapter.js +99 -177
- package/dist/adapters/lark/lark.adapter.js.map +1 -1
- package/dist/adapters/lark/lark.controller.d.ts +10 -1
- package/dist/adapters/lark/lark.controller.d.ts.map +1 -1
- package/dist/adapters/lark/lark.controller.js +48 -14
- package/dist/adapters/lark/lark.controller.js.map +1 -1
- package/dist/adapters/slack/slack-block.builder.d.ts +8 -0
- package/dist/adapters/slack/slack-block.builder.d.ts.map +1 -0
- package/dist/adapters/slack/slack-block.builder.js +117 -0
- package/dist/adapters/slack/slack-block.builder.js.map +1 -0
- package/dist/adapters/slack/slack-message.parser.d.ts +15 -0
- package/dist/adapters/slack/slack-message.parser.d.ts.map +1 -0
- package/dist/adapters/slack/slack-message.parser.js +158 -0
- package/dist/adapters/slack/slack-message.parser.js.map +1 -0
- package/dist/adapters/slack/slack-signature.d.ts +7 -0
- package/dist/adapters/slack/slack-signature.d.ts.map +1 -0
- package/dist/adapters/slack/slack-signature.js +59 -0
- package/dist/adapters/slack/slack-signature.js.map +1 -0
- package/dist/adapters/slack/slack.adapter.d.ts +67 -13
- package/dist/adapters/slack/slack.adapter.d.ts.map +1 -1
- package/dist/adapters/slack/slack.adapter.js +468 -19
- package/dist/adapters/slack/slack.adapter.js.map +1 -1
- package/dist/adapters/slack/slack.controller.d.ts +20 -0
- package/dist/adapters/slack/slack.controller.d.ts.map +1 -0
- package/dist/adapters/slack/slack.controller.js +257 -0
- package/dist/adapters/slack/slack.controller.js.map +1 -0
- package/dist/app.module.d.ts.map +1 -1
- package/dist/app.module.js +4 -0
- package/dist/app.module.js.map +1 -1
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +1 -0
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.module.d.ts.map +1 -1
- package/dist/auth/auth.module.js +4 -5
- package/dist/auth/auth.module.js.map +1 -1
- package/dist/auth/auth.service.d.ts +9 -2
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +50 -79
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/authenticated-request.d.ts +10 -0
- package/dist/auth/authenticated-request.d.ts.map +1 -0
- package/dist/auth/authenticated-request.js +3 -0
- package/dist/auth/authenticated-request.js.map +1 -0
- package/dist/auth/extract-user.middleware.d.ts.map +1 -1
- package/dist/auth/extract-user.middleware.js +2 -1
- package/dist/auth/extract-user.middleware.js.map +1 -1
- package/dist/auth/guards/jwt-auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/jwt-auth.guard.js +5 -2
- package/dist/auth/guards/jwt-auth.guard.js.map +1 -1
- package/dist/auth/guards/project-role.guard.d.ts.map +1 -1
- package/dist/auth/guards/project-role.guard.js +6 -8
- package/dist/auth/guards/project-role.guard.js.map +1 -1
- package/dist/auth/jwt.strategy.d.ts +6 -3
- package/dist/auth/jwt.strategy.d.ts.map +1 -1
- package/dist/auth/jwt.strategy.js +15 -5
- package/dist/auth/jwt.strategy.js.map +1 -1
- package/dist/common/command-parser.d.ts +29 -0
- package/dist/common/command-parser.d.ts.map +1 -0
- package/dist/common/command-parser.js +133 -0
- package/dist/common/command-parser.js.map +1 -0
- package/dist/common/config.d.ts +17 -0
- package/dist/common/config.d.ts.map +1 -0
- package/dist/common/config.js +47 -0
- package/dist/common/config.js.map +1 -0
- package/dist/common/crypto.service.d.ts +4 -1
- package/dist/common/crypto.service.d.ts.map +1 -1
- package/dist/common/crypto.service.js +14 -7
- package/dist/common/crypto.service.js.map +1 -1
- package/dist/common/error-filter.d.ts +1 -0
- package/dist/common/error-filter.d.ts.map +1 -1
- package/dist/common/error-filter.js +6 -2
- package/dist/common/error-filter.js.map +1 -1
- package/dist/common/git-utils.d.ts +9 -0
- package/dist/common/git-utils.d.ts.map +1 -0
- package/dist/common/git-utils.js +41 -0
- package/dist/common/git-utils.js.map +1 -0
- package/dist/common/health.controller.d.ts.map +1 -1
- package/dist/common/health.controller.js +3 -5
- package/dist/common/health.controller.js.map +1 -1
- package/dist/common/machine-utils.d.ts +32 -0
- package/dist/common/machine-utils.d.ts.map +1 -0
- package/dist/common/machine-utils.js +12 -0
- package/dist/common/machine-utils.js.map +1 -0
- package/dist/common/pagination.d.ts +12 -5
- package/dist/common/pagination.d.ts.map +1 -1
- package/dist/common/pagination.js +27 -17
- package/dist/common/pagination.js.map +1 -1
- package/dist/common/project-validation.d.ts +7 -0
- package/dist/common/project-validation.d.ts.map +1 -0
- package/dist/common/project-validation.js +86 -0
- package/dist/common/project-validation.js.map +1 -0
- package/dist/common/rate-limit.guard.d.ts +4 -3
- package/dist/common/rate-limit.guard.d.ts.map +1 -1
- package/dist/common/rate-limit.guard.js +14 -5
- package/dist/common/rate-limit.guard.js.map +1 -1
- package/dist/common/sql-utils.d.ts +6 -0
- package/dist/common/sql-utils.d.ts.map +1 -0
- package/dist/common/sql-utils.js +11 -0
- package/dist/common/sql-utils.js.map +1 -0
- package/dist/common/string-utils.d.ts +6 -0
- package/dist/common/string-utils.d.ts.map +1 -0
- package/dist/common/string-utils.js +15 -0
- package/dist/common/string-utils.js.map +1 -0
- package/dist/common/worker-utils.d.ts +31 -0
- package/dist/common/worker-utils.d.ts.map +1 -0
- package/dist/common/worker-utils.js +12 -0
- package/dist/common/worker-utils.js.map +1 -0
- package/dist/database/base.repository.d.ts +56 -0
- package/dist/database/base.repository.d.ts.map +1 -0
- package/dist/database/base.repository.js +82 -0
- package/dist/database/base.repository.js.map +1 -0
- package/dist/database/database.service.d.ts.map +1 -1
- package/dist/database/database.service.js +9 -1
- package/dist/database/database.service.js.map +1 -1
- package/dist/database/migration-runner.d.ts.map +1 -1
- package/dist/database/migration-runner.js +2 -1
- package/dist/database/migration-runner.js.map +1 -1
- package/dist/database/repositories/audit-log.repository.d.ts.map +1 -1
- package/dist/database/repositories/audit-log.repository.js +16 -18
- package/dist/database/repositories/audit-log.repository.js.map +1 -1
- package/dist/database/repositories/bot.repository.d.ts +18 -32
- package/dist/database/repositories/bot.repository.d.ts.map +1 -1
- package/dist/database/repositories/bot.repository.js +42 -21
- package/dist/database/repositories/bot.repository.js.map +1 -1
- package/dist/database/repositories/developer-token.repository.d.ts +7 -17
- package/dist/database/repositories/developer-token.repository.d.ts.map +1 -1
- package/dist/database/repositories/developer-token.repository.js +24 -15
- package/dist/database/repositories/developer-token.repository.js.map +1 -1
- package/dist/database/repositories/developer.repository.d.ts +5 -1
- package/dist/database/repositories/developer.repository.d.ts.map +1 -1
- package/dist/database/repositories/developer.repository.js +60 -49
- package/dist/database/repositories/developer.repository.js.map +1 -1
- package/dist/database/repositories/machine.repository.d.ts.map +1 -1
- package/dist/database/repositories/machine.repository.js +2 -7
- package/dist/database/repositories/machine.repository.js.map +1 -1
- package/dist/database/repositories/notification.repository.d.ts +1 -0
- package/dist/database/repositories/notification.repository.d.ts.map +1 -1
- package/dist/database/repositories/notification.repository.js +25 -20
- package/dist/database/repositories/notification.repository.js.map +1 -1
- package/dist/database/repositories/project-member.repository.d.ts +7 -16
- package/dist/database/repositories/project-member.repository.d.ts.map +1 -1
- package/dist/database/repositories/project-member.repository.js +34 -24
- package/dist/database/repositories/project-member.repository.js.map +1 -1
- package/dist/database/repositories/project.repository.d.ts +2 -1
- package/dist/database/repositories/project.repository.d.ts.map +1 -1
- package/dist/database/repositories/project.repository.js +70 -71
- package/dist/database/repositories/project.repository.js.map +1 -1
- package/dist/database/repositories/session.repository.d.ts.map +1 -1
- package/dist/database/repositories/session.repository.js +22 -25
- package/dist/database/repositories/session.repository.js.map +1 -1
- package/dist/database/repositories/task.repository.d.ts +31 -7
- package/dist/database/repositories/task.repository.d.ts.map +1 -1
- package/dist/database/repositories/task.repository.js +134 -86
- package/dist/database/repositories/task.repository.js.map +1 -1
- package/dist/database/repositories/worker-token.repository.d.ts.map +1 -1
- package/dist/database/repositories/worker-token.repository.js +18 -16
- package/dist/database/repositories/worker-token.repository.js.map +1 -1
- package/dist/database/repositories/worker.repository.d.ts +50 -0
- package/dist/database/repositories/worker.repository.d.ts.map +1 -0
- package/dist/database/repositories/worker.repository.js +215 -0
- package/dist/database/repositories/worker.repository.js.map +1 -0
- package/dist/database/repositories/workspace.repository.d.ts +3 -2
- package/dist/database/repositories/workspace.repository.d.ts.map +1 -1
- package/dist/database/repositories/workspace.repository.js +29 -21
- package/dist/database/repositories/workspace.repository.js.map +1 -1
- package/dist/database/repository.module.d.ts +3 -0
- package/dist/database/repository.module.d.ts.map +1 -0
- package/dist/database/repository.module.js +45 -0
- package/dist/database/repository.module.js.map +1 -0
- package/dist/dispatcher/capability.service.d.ts +19 -14
- package/dist/dispatcher/capability.service.d.ts.map +1 -1
- package/dist/dispatcher/capability.service.js +77 -69
- package/dist/dispatcher/capability.service.js.map +1 -1
- package/dist/dispatcher/cleanup.service.d.ts +1 -1
- package/dist/dispatcher/cleanup.service.d.ts.map +1 -1
- package/dist/dispatcher/cleanup.service.js +13 -13
- package/dist/dispatcher/cleanup.service.js.map +1 -1
- package/dist/dispatcher/dedup.service.d.ts +17 -3
- package/dist/dispatcher/dedup.service.d.ts.map +1 -1
- package/dist/dispatcher/dedup.service.js +76 -82
- package/dist/dispatcher/dedup.service.js.map +1 -1
- package/dist/dispatcher/dispatcher.module.d.ts.map +1 -1
- package/dist/dispatcher/dispatcher.module.js +11 -18
- package/dist/dispatcher/dispatcher.module.js.map +1 -1
- package/dist/dispatcher/dispatcher.service.d.ts +14 -116
- package/dist/dispatcher/dispatcher.service.d.ts.map +1 -1
- package/dist/dispatcher/dispatcher.service.js +62 -940
- package/dist/dispatcher/dispatcher.service.js.map +1 -1
- package/dist/dispatcher/dispatcher.types.d.ts +33 -0
- package/dist/dispatcher/dispatcher.types.d.ts.map +1 -0
- package/dist/dispatcher/dispatcher.types.js +3 -0
- package/dist/dispatcher/dispatcher.types.js.map +1 -0
- package/dist/dispatcher/heartbeat.service.d.ts +17 -10
- package/dist/dispatcher/heartbeat.service.d.ts.map +1 -1
- package/dist/dispatcher/heartbeat.service.js +47 -51
- package/dist/dispatcher/heartbeat.service.js.map +1 -1
- package/dist/dispatcher/pty-relay.service.d.ts.map +1 -1
- package/dist/dispatcher/pty-relay.service.js +7 -15
- package/dist/dispatcher/pty-relay.service.js.map +1 -1
- package/dist/dispatcher/reconciler.d.ts +18 -8
- package/dist/dispatcher/reconciler.d.ts.map +1 -1
- package/dist/dispatcher/reconciler.js +219 -130
- package/dist/dispatcher/reconciler.js.map +1 -1
- package/dist/dispatcher/scheduler.service.d.ts +15 -9
- package/dist/dispatcher/scheduler.service.d.ts.map +1 -1
- package/dist/dispatcher/scheduler.service.js +95 -53
- package/dist/dispatcher/scheduler.service.js.map +1 -1
- package/dist/dispatcher/state-machine.d.ts.map +1 -1
- package/dist/dispatcher/state-machine.js +1 -5
- package/dist/dispatcher/state-machine.js.map +1 -1
- package/dist/dispatcher/task-creation.service.d.ts +30 -0
- package/dist/dispatcher/task-creation.service.d.ts.map +1 -0
- package/dist/dispatcher/task-creation.service.js +242 -0
- package/dist/dispatcher/task-creation.service.js.map +1 -0
- package/dist/dispatcher/task-lifecycle.service.d.ts +63 -0
- package/dist/dispatcher/task-lifecycle.service.d.ts.map +1 -0
- package/dist/dispatcher/task-lifecycle.service.js +584 -0
- package/dist/dispatcher/task-lifecycle.service.js.map +1 -0
- package/dist/dispatcher/task-log-batcher.d.ts.map +1 -1
- package/dist/dispatcher/task-log-batcher.js +4 -11
- package/dist/dispatcher/task-log-batcher.js.map +1 -1
- package/dist/dispatcher/worker-auth.service.d.ts +29 -0
- package/dist/dispatcher/worker-auth.service.d.ts.map +1 -0
- package/dist/dispatcher/worker-auth.service.js +296 -0
- package/dist/dispatcher/worker-auth.service.js.map +1 -0
- package/dist/dispatcher/worker-connection.manager.d.ts +15 -15
- package/dist/dispatcher/worker-connection.manager.d.ts.map +1 -1
- package/dist/dispatcher/worker-connection.manager.js +35 -43
- package/dist/dispatcher/worker-connection.manager.js.map +1 -1
- package/dist/dispatcher/worker-selector.d.ts +18 -0
- package/dist/dispatcher/worker-selector.d.ts.map +1 -0
- package/dist/dispatcher/worker-selector.js +150 -0
- package/dist/dispatcher/worker-selector.js.map +1 -0
- package/dist/events/event-types.d.ts +31 -0
- package/dist/events/event-types.d.ts.map +1 -0
- package/dist/events/event-types.js +16 -0
- package/dist/events/event-types.js.map +1 -0
- package/dist/events/events.module.d.ts +7 -0
- package/dist/events/events.module.d.ts.map +1 -0
- package/dist/events/events.module.js +26 -0
- package/dist/events/events.module.js.map +1 -0
- package/dist/main.js +22 -0
- package/dist/main.js.map +1 -1
- package/dist/notifier/debouncer.d.ts +1 -1
- package/dist/notifier/debouncer.d.ts.map +1 -1
- package/dist/notifier/debouncer.js +2 -1
- package/dist/notifier/debouncer.js.map +1 -1
- package/dist/notifier/notification-consumer.d.ts +1 -1
- package/dist/notifier/notification-consumer.d.ts.map +1 -1
- package/dist/notifier/notification-consumer.js +5 -5
- package/dist/notifier/notification-consumer.js.map +1 -1
- package/dist/notifier/notifier.module.d.ts.map +1 -1
- package/dist/notifier/notifier.module.js +0 -6
- package/dist/notifier/notifier.module.js.map +1 -1
- package/dist/notifier/notifier.service.d.ts +1 -1
- package/dist/notifier/notifier.service.d.ts.map +1 -1
- package/dist/notifier/notifier.service.js +7 -9
- package/dist/notifier/notifier.service.js.map +1 -1
- package/dist/notifier/template.service.d.ts +1 -1
- package/dist/notifier/template.service.d.ts.map +1 -1
- package/dist/notifier/template.service.js +6 -10
- package/dist/notifier/template.service.js.map +1 -1
- package/dist/redis/redis.service.d.ts.map +1 -1
- package/dist/redis/redis.service.js +2 -2
- package/dist/redis/redis.service.js.map +1 -1
- package/dist/web/admin/admin-audit.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-audit.controller.js +2 -1
- package/dist/web/admin/admin-audit.controller.js.map +1 -1
- package/dist/web/admin/admin-bot.controller.d.ts +11 -48
- package/dist/web/admin/admin-bot.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-bot.controller.js +50 -18
- package/dist/web/admin/admin-bot.controller.js.map +1 -1
- package/dist/web/admin/admin-developer.controller.d.ts +14 -27
- package/dist/web/admin/admin-developer.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-developer.controller.js +62 -28
- package/dist/web/admin/admin-developer.controller.js.map +1 -1
- package/dist/web/admin/admin-machine.controller.d.ts +1 -8
- package/dist/web/admin/admin-machine.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-machine.controller.js +3 -6
- package/dist/web/admin/admin-machine.controller.js.map +1 -1
- package/dist/web/admin/admin-project.controller.d.ts +9 -30
- package/dist/web/admin/admin-project.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-project.controller.js +15 -60
- package/dist/web/admin/admin-project.controller.js.map +1 -1
- package/dist/web/admin/admin-settings.controller.d.ts +7 -10
- package/dist/web/admin/admin-settings.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-settings.controller.js +14 -6
- package/dist/web/admin/admin-settings.controller.js.map +1 -1
- package/dist/web/admin/admin-token.controller.d.ts +6 -13
- package/dist/web/admin/admin-token.controller.d.ts.map +1 -1
- package/dist/web/admin/admin-token.controller.js +15 -27
- package/dist/web/admin/admin-token.controller.js.map +1 -1
- package/dist/web/admin/admin-worker.controller.d.ts +26 -0
- package/dist/web/admin/admin-worker.controller.d.ts.map +1 -0
- package/dist/web/admin/admin-worker.controller.js +184 -0
- package/dist/web/admin/admin-worker.controller.js.map +1 -0
- package/dist/web/dashboard.controller.d.ts +6 -12
- package/dist/web/dashboard.controller.d.ts.map +1 -1
- package/dist/web/dashboard.controller.js +30 -18
- package/dist/web/dashboard.controller.js.map +1 -1
- package/dist/web/dashboard.service.d.ts +21 -12
- package/dist/web/dashboard.service.d.ts.map +1 -1
- package/dist/web/dashboard.service.js +169 -119
- package/dist/web/dashboard.service.js.map +1 -1
- package/dist/web/event.gateway.d.ts +32 -0
- package/dist/web/event.gateway.d.ts.map +1 -0
- package/dist/web/event.gateway.js +168 -0
- package/dist/web/event.gateway.js.map +1 -0
- package/dist/web/frame-handlers/frame-handler.interface.d.ts +24 -0
- package/dist/web/frame-handlers/frame-handler.interface.d.ts.map +1 -0
- package/dist/web/frame-handlers/frame-handler.interface.js +3 -0
- package/dist/web/frame-handlers/frame-handler.interface.js.map +1 -0
- package/dist/web/frame-handlers/frame-handler.registry.d.ts +16 -0
- package/dist/web/frame-handlers/frame-handler.registry.d.ts.map +1 -0
- package/dist/web/frame-handlers/frame-handler.registry.js +39 -0
- package/dist/web/frame-handlers/frame-handler.registry.js.map +1 -0
- package/dist/web/frame-handlers/heartbeat.handler.d.ts +13 -0
- package/dist/web/frame-handlers/heartbeat.handler.d.ts.map +1 -0
- package/dist/web/frame-handlers/heartbeat.handler.js +35 -0
- package/dist/web/frame-handlers/heartbeat.handler.js.map +1 -0
- package/dist/web/frame-handlers/index.d.ts +7 -0
- package/dist/web/frame-handlers/index.d.ts.map +1 -0
- package/dist/web/frame-handlers/index.js +14 -0
- package/dist/web/frame-handlers/index.js.map +1 -0
- package/dist/web/frame-handlers/progress.handler.d.ts +25 -0
- package/dist/web/frame-handlers/progress.handler.d.ts.map +1 -0
- package/dist/web/frame-handlers/progress.handler.js +69 -0
- package/dist/web/frame-handlers/progress.handler.js.map +1 -0
- package/dist/web/frame-handlers/stage-confirm.handler.d.ts +15 -0
- package/dist/web/frame-handlers/stage-confirm.handler.d.ts.map +1 -0
- package/dist/web/frame-handlers/stage-confirm.handler.js +39 -0
- package/dist/web/frame-handlers/stage-confirm.handler.js.map +1 -0
- package/dist/web/frame-handlers/tunnel.handler.d.ts +10 -0
- package/dist/web/frame-handlers/tunnel.handler.d.ts.map +1 -0
- package/dist/web/frame-handlers/tunnel.handler.js +31 -0
- package/dist/web/frame-handlers/tunnel.handler.js.map +1 -0
- package/dist/web/interaction.service.d.ts +0 -4
- package/dist/web/interaction.service.d.ts.map +1 -1
- package/dist/web/interaction.service.js +0 -10
- package/dist/web/interaction.service.js.map +1 -1
- package/dist/web/machine.controller.d.ts +1 -8
- package/dist/web/machine.controller.d.ts.map +1 -1
- package/dist/web/machine.controller.js +6 -9
- package/dist/web/machine.controller.js.map +1 -1
- package/dist/web/notification.controller.d.ts +1 -8
- package/dist/web/notification.controller.d.ts.map +1 -1
- package/dist/web/notification.controller.js +3 -2
- package/dist/web/notification.controller.js.map +1 -1
- package/dist/web/profile.controller.d.ts +19 -10
- package/dist/web/profile.controller.d.ts.map +1 -1
- package/dist/web/profile.controller.js +100 -13
- package/dist/web/profile.controller.js.map +1 -1
- package/dist/web/project-member.service.d.ts +16 -0
- package/dist/web/project-member.service.d.ts.map +1 -0
- package/dist/web/project-member.service.js +90 -0
- package/dist/web/project-member.service.js.map +1 -0
- package/dist/web/project.controller.d.ts +43 -26
- package/dist/web/project.controller.d.ts.map +1 -1
- package/dist/web/project.controller.js +73 -46
- package/dist/web/project.controller.js.map +1 -1
- package/dist/web/pty.gateway.d.ts +9 -3
- package/dist/web/pty.gateway.d.ts.map +1 -1
- package/dist/web/pty.gateway.js +46 -18
- package/dist/web/pty.gateway.js.map +1 -1
- package/dist/web/search.service.d.ts +9 -2
- package/dist/web/search.service.d.ts.map +1 -1
- package/dist/web/search.service.js +53 -26
- package/dist/web/search.service.js.map +1 -1
- package/dist/web/task.controller.d.ts +15 -24
- package/dist/web/task.controller.d.ts.map +1 -1
- package/dist/web/task.controller.js +70 -53
- package/dist/web/task.controller.js.map +1 -1
- package/dist/web/tunnel.service.d.ts +74 -0
- package/dist/web/tunnel.service.d.ts.map +1 -0
- package/dist/web/tunnel.service.js +250 -0
- package/dist/web/tunnel.service.js.map +1 -0
- package/dist/web/web-event.service.d.ts +25 -0
- package/dist/web/web-event.service.d.ts.map +1 -0
- package/dist/web/web-event.service.js +116 -0
- package/dist/web/web-event.service.js.map +1 -0
- package/dist/web/web.module.d.ts.map +1 -1
- package/dist/web/web.module.js +13 -28
- package/dist/web/web.module.js.map +1 -1
- package/dist/web/worker-channel.gateway.d.ts +10 -18
- package/dist/web/worker-channel.gateway.d.ts.map +1 -1
- package/dist/web/worker-channel.gateway.js +70 -144
- package/dist/web/worker-channel.gateway.js.map +1 -1
- package/dist/web/worker-web.controller.d.ts +15 -0
- package/dist/web/worker-web.controller.d.ts.map +1 -0
- package/dist/web/worker-web.controller.js +143 -0
- package/dist/web/worker-web.controller.js.map +1 -0
- package/dist/web/worker.controller.d.ts +3 -3
- package/dist/web/worker.controller.d.ts.map +1 -1
- package/dist/web/worker.controller.js +8 -8
- package/dist/web/worker.controller.js.map +1 -1
- package/dist/web/workspace.controller.d.ts +8 -33
- package/dist/web/workspace.controller.d.ts.map +1 -1
- package/dist/web/workspace.controller.js +93 -205
- package/dist/web/workspace.controller.js.map +1 -1
- package/package.json +10 -2
- package/public/apple-touch-icon-120x120.png +0 -0
- package/public/apple-touch-icon-152x152.png +0 -0
- package/public/apple-touch-icon-180x180.png +0 -0
- package/public/assets/AccessTokensPage-DPQB2fbi.js +1 -0
- package/public/assets/AdminPage-BqVelYNu.js +1 -0
- package/public/assets/ApiReferencePage-CiGvbLxL.js +1 -0
- package/public/assets/AuditLogPage-DSo4jVYm.js +6 -0
- package/public/assets/BindPlatformPage-CTqzpOmt.js +1 -0
- package/public/assets/BotManage-CIR0rrK7.js +6 -0
- package/public/assets/CliReferencePage-C8GmlwUz.js +14 -0
- package/public/assets/DeveloperManage-r6y2AoB4.js +16 -0
- package/public/assets/EditProjectPage-7WCsNltj.js +2 -0
- package/public/assets/{EmptyState-CvmhFgWJ.js → EmptyState-D3foEiul.js} +1 -1
- package/public/assets/HomePage-D4yv4orb.js +1 -0
- package/public/assets/InfoRow-DhdTYoY9.js +1 -0
- package/public/assets/LandingPage-CqS0E2eC.js +43 -0
- package/public/assets/LoginPage-DDXkdcz_.js +1 -0
- package/public/assets/MetricBar-DMMHfS0A.js +1 -0
- package/public/assets/{NotFoundPage-BuiAS4g4.js → NotFoundPage-D5x5BrlX.js} +1 -1
- package/public/assets/OnboardingGuide-D8RyPcEd.js +1 -0
- package/public/assets/PipelineEditorPage-y2-Q8ofQ.js +3 -0
- package/public/assets/ProfilePage-DN7usHOi.js +1 -0
- package/public/assets/ProjectDetailPage-DJexg49z.js +7 -0
- package/public/assets/ProjectListPage-Bz7I2D0H.js +6 -0
- package/public/assets/QuickAuth-Dr0Q50ld.js +1 -0
- package/public/assets/{RemoveMemberConfirmDialog-DS9z6jQT.js → RemoveMemberConfirmDialog-BCrue0AP.js} +2 -2
- package/public/assets/Select-BnV8yZlD.js +6 -0
- package/public/assets/SettingsPage-HaUCcsgl.js +6 -0
- package/public/assets/{Skeleton-CcVqz28_.js → Skeleton-DUgWc2LJ.js} +1 -1
- package/public/assets/SkillPage-BInwZTQh.js +1 -0
- package/public/assets/TaskDetailPage-CfwEj1hy.js +31 -0
- package/public/assets/TaskListPage-Dh59ldSZ.js +1 -0
- package/public/assets/TaskStatusBadge-DuOoGIwE.js +1 -0
- package/public/assets/TerminalHomePage-BwXJjr-a.js +16 -0
- package/public/assets/TokenManage-B0Cpv6SO.js +1 -0
- package/public/assets/TotpSetupPage-MSCCURj9.js +9 -0
- package/public/assets/WorkerDetailPage-R2veIzKo.js +1 -0
- package/public/assets/WorkerListPage-CserMjGO.js +6 -0
- package/public/assets/WorkerSetupGuidePage-SqO2lzVa.js +11 -0
- package/public/assets/{arrow-left-CVKez32c.js → arrow-left-DklRsENx.js} +1 -1
- package/public/assets/{arrow-right-g7hTftEi.js → arrow-right-MDrzFe3K.js} +1 -1
- package/public/assets/{bot-DYvBcsZn.js → bot-DPaziJPf.js} +1 -1
- package/public/assets/{chevron-right-COxU2yxz.js → chevron-right-CqyufMDW.js} +1 -1
- package/public/assets/{copy-BGttVgA1.js → copy-BUH7P2Hf.js} +1 -1
- package/public/assets/date-BdNtiQTP.js +1 -0
- package/public/assets/{external-link-DXlCfUjE.js → external-link-ChPgQ7N_.js} +1 -1
- package/public/assets/index-BS0Fbx5V.css +1 -0
- package/public/assets/index-vL7aQJNr.js +225 -0
- package/public/assets/{key-3eDVdGih.js → key-CxvwwHnW.js} +1 -1
- package/public/assets/{loader-circle-DPm92ETj.js → loader-circle-DS5g1-Od.js} +1 -1
- package/public/assets/password-CHk45-jw.js +1 -0
- package/public/assets/{pencil-Bs3PwH2W.js → pencil-B6spIBcw.js} +1 -1
- package/public/assets/{plus-d-PLzbVX.js → plus-Bnd1Vz2Y.js} +1 -1
- package/public/assets/{rotate-ccw-Cus8CABi.js → rotate-ccw-CgcLAXNR.js} +1 -1
- package/public/assets/{scroll-text-CV3wlIy2.js → scroll-text-CecZ0Fk5.js} +1 -1
- package/public/assets/{settings-Ccijf48b.js → settings-C1uOD3PZ.js} +1 -1
- package/public/assets/status-colors-BPEUp90-.js +1 -0
- package/public/assets/string-B39tzdVK.js +1 -0
- package/public/assets/task-constants-BbFyCyKk.js +14 -0
- package/public/assets/task.store-BE6fEPu4.js +1 -0
- package/public/assets/{trash-2-bRJ-xwtq.js → trash-2-A2FsT1yG.js} +1 -1
- package/public/assets/useFetch-vGZMAvGi.js +1 -0
- package/public/assets/{users-LNQqKSEN.js → users-CEdRS_A3.js} +1 -1
- package/public/assets/wifi-D60NkK6F.js +6 -0
- package/public/assets/zap-DXw1NrWz.js +6 -0
- package/public/icon-192x192.png +0 -0
- package/public/icon-512x512.png +0 -0
- package/public/icon-maskable-192x192.png +0 -0
- package/public/icon-maskable-512x512.png +0 -0
- package/public/index.html +21 -4
- package/public/manifest.webmanifest +1 -0
- package/public/og-image.png +0 -0
- package/public/registerSW.js +1 -0
- package/public/sw.js +1 -0
- package/public/workbox-6e9b121d.js +1 -0
- package/database/migrations/002-add-indexes.sql +0 -17
- package/database/migrations/003-add-settings-table.sql +0 -4
- package/database/migrations/004-add-developer-id-index.sql +0 -5
- package/database/migrations/005-add-worker-version.sql +0 -2
- package/database/migrations/006-add-decommission-fields.sql +0 -2
- package/database/migrations/007-add-ssh-url.sql +0 -1
- package/public/assets/AccessTokensPage-Cb5hGBfN.js +0 -1
- package/public/assets/AdminPage-C7Xytkfo.js +0 -1
- package/public/assets/ApiReferencePage-DLGVc4xN.js +0 -1
- package/public/assets/AuditLogPage-BgqsUJ7x.js +0 -6
- package/public/assets/BotManage-Ds9DYQZA.js +0 -6
- package/public/assets/CliReferencePage-WXinn_69.js +0 -8
- package/public/assets/DeveloperManage-saSq3Hfx.js +0 -16
- package/public/assets/EditProjectPage-C4xWYLTo.js +0 -2
- package/public/assets/HomePage-y20pQ52r.js +0 -1
- package/public/assets/LandingPage-CB5BBbdI.js +0 -36
- package/public/assets/LoginPage-DLljhJkQ.js +0 -1
- package/public/assets/MachineDetailPage-CPm9tfdp.js +0 -1
- package/public/assets/MachineListPage-D-nhVz-m.js +0 -6
- package/public/assets/PipelineEditorPage-B9GgqAju.js +0 -3
- package/public/assets/ProfilePage-DASpeJq6.js +0 -1
- package/public/assets/ProjectDetailPage-DWZyNwTV.js +0 -7
- package/public/assets/ProjectListPage-BUMX3Dxa.js +0 -6
- package/public/assets/QuickAuth-B9mvq7ht.js +0 -1
- package/public/assets/Select-A7PXobk_.js +0 -6
- package/public/assets/SettingsPage-WGzxUbGp.js +0 -6
- package/public/assets/SkillPage-BIt2kF3W.js +0 -1
- package/public/assets/TaskDetailPage-CePyNfp6.js +0 -44
- package/public/assets/TaskListPage-MO4_PSve.js +0 -1
- package/public/assets/TaskStatusBadge-QtQUDscM.js +0 -1
- package/public/assets/TokenManage-Sf0RGymw.js +0 -1
- package/public/assets/TotpSetupPage-D-rbEYLf.js +0 -9
- package/public/assets/WorkerSetupGuidePage-D6Fv1MQo.js +0 -16
- package/public/assets/index-CDBuOPx4.js +0 -212
- package/public/assets/index-CQojj7Zu.css +0 -1
- package/public/assets/protocol-C5uQmiiB.js +0 -1
- package/public/assets/task.store-CvjSr507.js +0 -1
- /package/public/assets/{TaskDetailPage-Beg8tuEN.css → task-constants-Beg8tuEN.css} +0 -0
|
@@ -1,104 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
2
|
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
3
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
4
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
5
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
6
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
7
|
};
|
|
24
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
-
var ownKeys = function(o) {
|
|
26
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
-
var ar = [];
|
|
28
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
-
return ar;
|
|
30
|
-
};
|
|
31
|
-
return ownKeys(o);
|
|
32
|
-
};
|
|
33
|
-
return function (mod) {
|
|
34
|
-
if (mod && mod.__esModule) return mod;
|
|
35
|
-
var result = {};
|
|
36
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
-
__setModuleDefault(result, mod);
|
|
38
|
-
return result;
|
|
39
|
-
};
|
|
40
|
-
})();
|
|
41
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
10
|
};
|
|
44
11
|
var DispatcherService_1;
|
|
45
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
13
|
exports.DispatcherService = void 0;
|
|
47
|
-
const common_1 = require("@nestjs/common");
|
|
48
|
-
const crypto = __importStar(require("node:crypto"));
|
|
49
14
|
const protocol_1 = require("@overlordai/protocol");
|
|
15
|
+
const common_1 = require("@nestjs/common");
|
|
16
|
+
const event_emitter_1 = require("@nestjs/event-emitter");
|
|
50
17
|
const task_repository_1 = require("../database/repositories/task.repository");
|
|
51
|
-
const
|
|
52
|
-
const project_repository_1 = require("../database/repositories/project.repository");
|
|
53
|
-
const project_member_repository_1 = require("../database/repositories/project-member.repository");
|
|
54
|
-
const worker_token_repository_1 = require("../database/repositories/worker-token.repository");
|
|
18
|
+
const worker_repository_1 = require("../database/repositories/worker.repository");
|
|
55
19
|
const audit_log_repository_1 = require("../database/repositories/audit-log.repository");
|
|
56
20
|
const developer_repository_1 = require("../database/repositories/developer.repository");
|
|
57
|
-
const
|
|
58
|
-
const auth_service_1 = require("../auth/auth.service");
|
|
59
|
-
const crypto_service_1 = require("../common/crypto.service");
|
|
60
|
-
const redis_service_1 = require("../redis/redis.service");
|
|
61
|
-
const state_machine_1 = require("./state-machine");
|
|
21
|
+
const event_types_1 = require("../events/event-types");
|
|
62
22
|
const dedup_service_1 = require("./dedup.service");
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const cleanup_service_1 = require("./cleanup.service");
|
|
66
|
-
const workspace_repository_1 = require("../database/repositories/workspace.repository");
|
|
23
|
+
const task_creation_service_1 = require("./task-creation.service");
|
|
24
|
+
const task_lifecycle_service_1 = require("./task-lifecycle.service");
|
|
67
25
|
let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
68
26
|
taskRepo;
|
|
69
|
-
|
|
70
|
-
projectRepo;
|
|
71
|
-
projectMemberRepo;
|
|
72
|
-
workerTokenRepo;
|
|
27
|
+
workerRepo;
|
|
73
28
|
auditLogRepo;
|
|
74
29
|
developerRepo;
|
|
75
|
-
databaseService;
|
|
76
|
-
authService;
|
|
77
|
-
cryptoService;
|
|
78
|
-
redis;
|
|
79
30
|
dedupService;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
workspaceRepo;
|
|
31
|
+
taskCreationService;
|
|
32
|
+
taskLifecycleService;
|
|
33
|
+
eventEmitter;
|
|
84
34
|
logger = new common_1.Logger(DispatcherService_1.name);
|
|
85
|
-
constructor(taskRepo,
|
|
35
|
+
constructor(taskRepo, workerRepo, auditLogRepo, developerRepo, dedupService, taskCreationService, taskLifecycleService, eventEmitter) {
|
|
86
36
|
this.taskRepo = taskRepo;
|
|
87
|
-
this.
|
|
88
|
-
this.projectRepo = projectRepo;
|
|
89
|
-
this.projectMemberRepo = projectMemberRepo;
|
|
90
|
-
this.workerTokenRepo = workerTokenRepo;
|
|
37
|
+
this.workerRepo = workerRepo;
|
|
91
38
|
this.auditLogRepo = auditLogRepo;
|
|
92
39
|
this.developerRepo = developerRepo;
|
|
93
|
-
this.databaseService = databaseService;
|
|
94
|
-
this.authService = authService;
|
|
95
|
-
this.cryptoService = cryptoService;
|
|
96
|
-
this.redis = redis;
|
|
97
40
|
this.dedupService = dedupService;
|
|
98
|
-
this.
|
|
99
|
-
this.
|
|
100
|
-
this.
|
|
101
|
-
this.workspaceRepo = workspaceRepo;
|
|
41
|
+
this.taskCreationService = taskCreationService;
|
|
42
|
+
this.taskLifecycleService = taskLifecycleService;
|
|
43
|
+
this.eventEmitter = eventEmitter;
|
|
102
44
|
}
|
|
103
45
|
// ---------------------------------------------------------------------------
|
|
104
46
|
// handleCommand — unified entry point from adapters
|
|
@@ -122,49 +64,20 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
122
64
|
return this.handleConfirmCommand(command);
|
|
123
65
|
case protocol_1.CommandType.LIST:
|
|
124
66
|
return this.handleListCommand(command);
|
|
125
|
-
case protocol_1.CommandType.PROGRESS:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
const task = this.taskRepo.findById(taskId);
|
|
131
|
-
if (!task) {
|
|
132
|
-
return { success: false, message: `Task #${taskId} not found` };
|
|
133
|
-
}
|
|
134
|
-
return { success: true, task };
|
|
135
|
-
}
|
|
136
|
-
case protocol_1.CommandType.LOGS: {
|
|
137
|
-
const taskId = command.args.taskId;
|
|
138
|
-
if (!taskId) {
|
|
139
|
-
return { success: false, message: 'taskId is required' };
|
|
140
|
-
}
|
|
141
|
-
const task = this.taskRepo.findById(taskId);
|
|
142
|
-
if (!task) {
|
|
143
|
-
return { success: false, message: `Task #${taskId} not found` };
|
|
144
|
-
}
|
|
145
|
-
return { success: true, task };
|
|
146
|
-
}
|
|
67
|
+
case protocol_1.CommandType.PROGRESS:
|
|
68
|
+
case protocol_1.CommandType.LOGS:
|
|
69
|
+
case protocol_1.CommandType.WORKSPACE:
|
|
70
|
+
return this.requireTask(command.args.taskId);
|
|
147
71
|
case protocol_1.CommandType.DRAIN:
|
|
148
72
|
return this.handleDrainCommand(command, true);
|
|
149
73
|
case protocol_1.CommandType.UNDRAIN:
|
|
150
74
|
return this.handleDrainCommand(command, false);
|
|
151
|
-
case protocol_1.CommandType.WORKSPACE: {
|
|
152
|
-
const taskId = command.args.taskId;
|
|
153
|
-
if (!taskId) {
|
|
154
|
-
return { success: false, message: 'taskId is required' };
|
|
155
|
-
}
|
|
156
|
-
const task = this.taskRepo.findById(taskId);
|
|
157
|
-
if (!task) {
|
|
158
|
-
return { success: false, message: `Task #${taskId} not found` };
|
|
159
|
-
}
|
|
160
|
-
return { success: true, task };
|
|
161
|
-
}
|
|
162
75
|
default:
|
|
163
76
|
return { success: false, message: `Unknown command type: ${command.type}` };
|
|
164
77
|
}
|
|
165
78
|
}
|
|
166
79
|
// ---------------------------------------------------------------------------
|
|
167
|
-
// Command handlers
|
|
80
|
+
// Command handlers — thin wrappers that delegate to focused services
|
|
168
81
|
// ---------------------------------------------------------------------------
|
|
169
82
|
async handleDevelopCommand(command) {
|
|
170
83
|
const { description, project: projectKeyOrAlias } = command.args;
|
|
@@ -174,11 +87,11 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
174
87
|
const request = {
|
|
175
88
|
description,
|
|
176
89
|
projectKey: projectKeyOrAlias ?? '',
|
|
177
|
-
|
|
90
|
+
workerId: command.args.worker,
|
|
178
91
|
developerId: command.user.id,
|
|
179
92
|
};
|
|
180
93
|
try {
|
|
181
|
-
const task = await this.createTask(request, command.user.id, {
|
|
94
|
+
const task = await this.taskCreationService.createTask(request, command.user.id, {
|
|
182
95
|
platform: command.platform,
|
|
183
96
|
msgId: command.source.msgId,
|
|
184
97
|
chatId: command.source.chatId,
|
|
@@ -200,12 +113,22 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
200
113
|
throw err;
|
|
201
114
|
}
|
|
202
115
|
}
|
|
116
|
+
requireTask(taskId) {
|
|
117
|
+
if (!taskId) {
|
|
118
|
+
return { success: false, message: 'taskId is required' };
|
|
119
|
+
}
|
|
120
|
+
const task = this.taskRepo.findById(taskId);
|
|
121
|
+
if (!task) {
|
|
122
|
+
return { success: false, message: `Task #${taskId} not found` };
|
|
123
|
+
}
|
|
124
|
+
return { success: true, task };
|
|
125
|
+
}
|
|
203
126
|
async handleCancelCommand(command) {
|
|
204
127
|
const taskId = command.args.taskId;
|
|
205
128
|
if (!taskId) {
|
|
206
129
|
return { success: false, message: 'taskId is required' };
|
|
207
130
|
}
|
|
208
|
-
await this.cancelTask(taskId, command.user.id);
|
|
131
|
+
await this.taskLifecycleService.cancelTask(taskId, command.user.id);
|
|
209
132
|
const task = this.taskRepo.findById(taskId);
|
|
210
133
|
return { success: true, task, message: `Task #${taskId} cancelled` };
|
|
211
134
|
}
|
|
@@ -215,7 +138,7 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
215
138
|
return { success: false, message: 'taskId is required' };
|
|
216
139
|
}
|
|
217
140
|
try {
|
|
218
|
-
const task = await this.retryTask(taskId, command.user.id);
|
|
141
|
+
const task = await this.taskLifecycleService.retryTask(taskId, command.user.id);
|
|
219
142
|
return { success: true, task, message: `Task #${taskId} retried` };
|
|
220
143
|
}
|
|
221
144
|
catch (err) {
|
|
@@ -238,18 +161,18 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
238
161
|
}
|
|
239
162
|
// Resolve pending confirm from Redis.
|
|
240
163
|
// Priority: reply-to msgId association, fallback to #taskId lookup.
|
|
241
|
-
const pendingData = await this.resolvePendingConfirm(taskId, command.user.id, command.source.msgId);
|
|
164
|
+
const pendingData = await this.taskLifecycleService.resolvePendingConfirm(taskId, command.user.id, command.source.msgId);
|
|
242
165
|
if (!pendingData) {
|
|
243
166
|
return { success: false, message: 'No pending confirmation found or expired' };
|
|
244
167
|
}
|
|
245
168
|
if (pendingData.action === 'create_task' && pendingData.request) {
|
|
246
169
|
// Confirmed dedup override — create the task anyway
|
|
247
|
-
const task = await this.createTaskInternal(pendingData.request, pendingData.createdBy, pendingData.source, true);
|
|
170
|
+
const task = await this.taskCreationService.createTaskInternal(pendingData.request, pendingData.createdBy, pendingData.source, true);
|
|
248
171
|
return { success: true, task, message: 'Task created (dedup override)' };
|
|
249
172
|
}
|
|
250
173
|
if (pendingData.action === 'retry' && pendingData.taskId) {
|
|
251
174
|
// Confirmed retry with existing MR
|
|
252
|
-
const task = await this.executeRetry(pendingData.taskId, pendingData.createdBy);
|
|
175
|
+
const task = await this.taskLifecycleService.executeRetry(pendingData.taskId, pendingData.createdBy);
|
|
253
176
|
return { success: true, task, message: `Task #${pendingData.taskId} retried (MR override)` };
|
|
254
177
|
}
|
|
255
178
|
return { success: false, message: 'Unknown confirm action' };
|
|
@@ -268,377 +191,36 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
268
191
|
return { success: true, tasks: filtered };
|
|
269
192
|
}
|
|
270
193
|
handleDrainCommand(command, drain) {
|
|
271
|
-
const
|
|
272
|
-
if (!
|
|
273
|
-
return { success: false, message: '
|
|
194
|
+
const workerName = command.args.worker;
|
|
195
|
+
if (!workerName) {
|
|
196
|
+
return { success: false, message: 'Worker name is required' };
|
|
274
197
|
}
|
|
275
|
-
const
|
|
276
|
-
if (!
|
|
277
|
-
return { success: false, message: `
|
|
198
|
+
const worker = this.workerRepo.findByName(workerName);
|
|
199
|
+
if (!worker) {
|
|
200
|
+
return { success: false, message: `Worker '${workerName}' not found` };
|
|
278
201
|
}
|
|
279
202
|
const developer = this.developerRepo.findById(command.user.id);
|
|
280
203
|
if (!developer || developer.role !== protocol_1.DeveloperRole.ADMIN) {
|
|
281
|
-
return { success: false, message: 'Only admins can drain/undrain
|
|
282
|
-
}
|
|
283
|
-
const newStatus = drain ? protocol_1.
|
|
284
|
-
|
|
285
|
-
this.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
});
|
|
291
|
-
return { success: true, message: `Machine '${machine.name}' ${drain ? 'drained' : 'undrained'}` };
|
|
292
|
-
}
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
// createTask
|
|
295
|
-
// ---------------------------------------------------------------------------
|
|
296
|
-
async createTask(request, createdBy, source) {
|
|
297
|
-
return this.createTaskInternal(request, createdBy, source, false);
|
|
298
|
-
}
|
|
299
|
-
async createTaskInternal(request, createdBy, source, skipDedup = false) {
|
|
300
|
-
// 1. Find project by key or alias
|
|
301
|
-
const project = this.resolveProject(request.projectKey);
|
|
302
|
-
if (!project) {
|
|
303
|
-
throw new common_1.NotFoundException(`Project '${request.projectKey}' not found`);
|
|
304
|
-
}
|
|
305
|
-
// 2. Authorization: check developer is a project member (or admin bypass)
|
|
306
|
-
const developer = this.developerRepo.findById(createdBy);
|
|
307
|
-
if (!developer) {
|
|
308
|
-
throw new common_1.NotFoundException(`Developer #${createdBy} not found`);
|
|
309
|
-
}
|
|
310
|
-
if (developer.role !== protocol_1.DeveloperRole.ADMIN) {
|
|
311
|
-
const membership = this.projectMemberRepo.findByProjectAndDeveloper(project.key, createdBy);
|
|
312
|
-
if (!membership) {
|
|
313
|
-
throw new common_1.ForbiddenException(`Developer '${developer.name}' is not a member of project '${project.key}'`);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
// 3. Compute fingerprint and check dedup
|
|
317
|
-
const fingerprint = this.dedupService.computeFingerprint(project.key, request.description);
|
|
318
|
-
if (!skipDedup) {
|
|
319
|
-
const existingTask = this.taskRepo.findActiveByFingerprint(fingerprint);
|
|
320
|
-
if (existingTask) {
|
|
321
|
-
// Store pending confirm in Redis
|
|
322
|
-
const confirmKey = `pending_confirm:dedup:${crypto.randomUUID()}`;
|
|
323
|
-
const pendingData = JSON.stringify({
|
|
324
|
-
action: 'create_task',
|
|
325
|
-
request: { ...request, projectKey: project.key },
|
|
326
|
-
createdBy,
|
|
327
|
-
source,
|
|
328
|
-
existingTaskId: existingTask.id,
|
|
329
|
-
});
|
|
330
|
-
await this.redis.setex(confirmKey, protocol_1.CONFIRM_PENDING_TTL_SEC, pendingData);
|
|
331
|
-
// Store index from taskId to confirmKey for lookup during confirm
|
|
332
|
-
await this.redis.setex(`pending_confirm_idx:task:${existingTask.id}`, protocol_1.CONFIRM_PENDING_TTL_SEC, confirmKey);
|
|
333
|
-
throw new common_1.ConflictException({
|
|
334
|
-
message: `A similar task already exists: #${existingTask.id} (${existingTask.status})`,
|
|
335
|
-
confirmRequired: {
|
|
336
|
-
reason: 'duplicate_fingerprint',
|
|
337
|
-
message: `Task #${existingTask.id} with the same description is already ${existingTask.status}. Create anyway?`,
|
|
338
|
-
pendingAction: 'create_task',
|
|
339
|
-
taskId: existingTask.id,
|
|
340
|
-
confirmKey,
|
|
341
|
-
},
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
// 4. Snapshot config (14 fields from project)
|
|
346
|
-
const configSnapshot = {
|
|
347
|
-
repoUrl: project.repoUrl,
|
|
348
|
-
sshUrl: project.sshUrl,
|
|
349
|
-
gitPlatform: project.gitPlatform,
|
|
350
|
-
defaultBranch: project.defaultBranch,
|
|
351
|
-
workspaceRoot: project.workspaceRoot,
|
|
352
|
-
setupCommands: project.setupCommands,
|
|
353
|
-
testCommand: project.testCommand,
|
|
354
|
-
agentType: project.agentType,
|
|
355
|
-
agentCommand: project.agentCommand,
|
|
356
|
-
agentEnv: project.agentEnv,
|
|
357
|
-
allowedTools: project.allowedTools,
|
|
358
|
-
maxTurns: project.maxTurns,
|
|
359
|
-
skillsPath: project.skillsPath,
|
|
360
|
-
pipeline: project.pipeline,
|
|
361
|
-
ptyOutputFilter: project.ptyOutputFilter,
|
|
362
|
-
};
|
|
363
|
-
// 5. Insert task (QUEUED)
|
|
364
|
-
let task;
|
|
365
|
-
try {
|
|
366
|
-
task = this.taskRepo.create({
|
|
367
|
-
description: request.description,
|
|
368
|
-
fingerprint,
|
|
369
|
-
projectKey: project.key,
|
|
370
|
-
machineId: request.machineId,
|
|
371
|
-
developerId: request.developerId ?? createdBy,
|
|
372
|
-
configSnapshot: JSON.stringify(configSnapshot),
|
|
373
|
-
sourcePlatform: source?.platform,
|
|
374
|
-
sourceMsgId: source?.msgId,
|
|
375
|
-
sourceChatId: source?.chatId,
|
|
376
|
-
sourceAppId: source?.appId,
|
|
377
|
-
createdBy,
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
catch (err) {
|
|
381
|
-
// Catch UNIQUE constraint violation on fingerprint (concurrent create)
|
|
382
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
383
|
-
if (errMsg.includes('UNIQUE') || errMsg.includes('unique')) {
|
|
384
|
-
throw new common_1.ConflictException('A task with the same fingerprint was just created concurrently');
|
|
385
|
-
}
|
|
386
|
-
throw err;
|
|
387
|
-
}
|
|
388
|
-
// 6. Enqueue to BullMQ
|
|
389
|
-
await this.schedulerService.enqueueTask(task.id);
|
|
390
|
-
this.logger.log(`Task #${task.id} created for project '${project.key}' by developer #${createdBy}`);
|
|
391
|
-
this.auditLogRepo.create({
|
|
392
|
-
userId: createdBy,
|
|
393
|
-
action: 'task_create',
|
|
394
|
-
resource: `task:${task.id}`,
|
|
395
|
-
detail: `Created task for project '${project.key}': ${request.description.slice(0, 100)}`,
|
|
396
|
-
});
|
|
397
|
-
return task;
|
|
398
|
-
}
|
|
399
|
-
// ---------------------------------------------------------------------------
|
|
400
|
-
// cancelTask
|
|
401
|
-
// ---------------------------------------------------------------------------
|
|
402
|
-
async cancelTask(taskId, cancelledBy) {
|
|
403
|
-
const task = this.taskRepo.findById(taskId);
|
|
404
|
-
if (!task) {
|
|
405
|
-
throw new common_1.NotFoundException(`Task #${taskId} not found`);
|
|
406
|
-
}
|
|
407
|
-
// Validate transition
|
|
408
|
-
state_machine_1.TaskStateMachine.assertTransition(task.status, protocol_1.TaskStatus.CANCELLED);
|
|
409
|
-
switch (task.status) {
|
|
410
|
-
case protocol_1.TaskStatus.QUEUED: {
|
|
411
|
-
// Remove from BullMQ — task stays QUEUED until we set CANCELLED below
|
|
412
|
-
this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, task.revision);
|
|
413
|
-
break;
|
|
414
|
-
}
|
|
415
|
-
case protocol_1.TaskStatus.RUNNING: {
|
|
416
|
-
// Send cancel to Worker via WS, wait ack with timeout
|
|
417
|
-
if (task.machineId) {
|
|
418
|
-
const acked = await this.sendCancelToWorkerWithAck(task.machineId, taskId);
|
|
419
|
-
if (!acked) {
|
|
420
|
-
this.logger.warn(`Cancel ack timeout for task #${taskId} on machine ${task.machineId}, forcing cancel`);
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
// Reload task to get latest revision (may have changed during ack wait)
|
|
424
|
-
const freshTask = this.taskRepo.findById(taskId);
|
|
425
|
-
if (freshTask && freshTask.status !== protocol_1.TaskStatus.CANCELLED) {
|
|
426
|
-
this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, freshTask.revision, { completedAt: new Date().toISOString() });
|
|
427
|
-
}
|
|
428
|
-
break;
|
|
429
|
-
}
|
|
430
|
-
case protocol_1.TaskStatus.ASSIGNED: {
|
|
431
|
-
// Re-read to catch ASSIGNED->RUNNING race between initial read and CAS
|
|
432
|
-
const freshAssigned = this.taskRepo.findById(taskId);
|
|
433
|
-
if (!freshAssigned || freshAssigned.status === protocol_1.TaskStatus.CANCELLED) {
|
|
434
|
-
break;
|
|
435
|
-
}
|
|
436
|
-
state_machine_1.TaskStateMachine.assertTransition(freshAssigned.status, protocol_1.TaskStatus.CANCELLED);
|
|
437
|
-
// Send cancel to Worker if assigned, set CANCELLED
|
|
438
|
-
if (freshAssigned.machineId) {
|
|
439
|
-
this.sendCancelToWorker(freshAssigned.machineId, taskId);
|
|
440
|
-
}
|
|
441
|
-
this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, freshAssigned.revision, { completedAt: new Date().toISOString() });
|
|
442
|
-
break;
|
|
443
|
-
}
|
|
444
|
-
case protocol_1.TaskStatus.SUSPENDED: {
|
|
445
|
-
// Direct set CANCELLED — no Worker ack needed
|
|
446
|
-
this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.CANCELLED, task.revision, { completedAt: new Date().toISOString() });
|
|
447
|
-
break;
|
|
448
|
-
}
|
|
449
|
-
default:
|
|
450
|
-
throw new common_1.BadRequestException(`Cannot cancel task #${taskId} in status ${task.status}`);
|
|
451
|
-
}
|
|
452
|
-
this.auditLogRepo.create({
|
|
453
|
-
userId: cancelledBy,
|
|
454
|
-
action: 'task_cancel',
|
|
455
|
-
resource: `task:${taskId}`,
|
|
456
|
-
detail: `Cancelled task #${taskId} from status ${task.status}`,
|
|
204
|
+
return { success: false, message: 'Only admins can drain/undrain workers' };
|
|
205
|
+
}
|
|
206
|
+
const newStatus = drain ? protocol_1.WorkerStatus.DRAINING : protocol_1.WorkerStatus.ONLINE;
|
|
207
|
+
const previousStatus = worker.status;
|
|
208
|
+
this.workerRepo.updateStatus(worker.id, newStatus);
|
|
209
|
+
this.eventEmitter.emit(event_types_1.DomainEvents.WORKER_STATUS_CHANGED, {
|
|
210
|
+
workerId: worker.id,
|
|
211
|
+
status: newStatus,
|
|
212
|
+
previousStatus,
|
|
457
213
|
});
|
|
458
|
-
// Schedule workspace cleanup for the cancelled task
|
|
459
|
-
if (task.machineId) {
|
|
460
|
-
const workspace = this.workspaceRepo.findByTaskId(taskId);
|
|
461
|
-
if (workspace) {
|
|
462
|
-
this.cleanupService.scheduleCleanup(taskId, task.machineId, workspace.path).catch((err) => {
|
|
463
|
-
this.logger.error(`Failed to schedule cleanup for cancelled task #${taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
this.logger.log(`Task #${taskId} cancelled by developer #${cancelledBy}`);
|
|
468
|
-
}
|
|
469
|
-
// ---------------------------------------------------------------------------
|
|
470
|
-
// markTaskRunning — transition ASSIGNED → RUNNING when worker starts executing
|
|
471
|
-
// ---------------------------------------------------------------------------
|
|
472
|
-
markTaskRunning(taskId) {
|
|
473
|
-
const task = this.taskRepo.findById(taskId);
|
|
474
|
-
if (!task || task.status !== protocol_1.TaskStatus.ASSIGNED)
|
|
475
|
-
return false;
|
|
476
|
-
const updated = this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.RUNNING, task.revision, { startedAt: new Date().toISOString() });
|
|
477
|
-
if (updated) {
|
|
478
|
-
this.auditLogRepo.create({
|
|
479
|
-
action: 'task_running_from_worker',
|
|
480
|
-
resource: `task:${taskId}`,
|
|
481
|
-
detail: `Task marked RUNNING via worker progress frame`,
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
return updated;
|
|
485
|
-
}
|
|
486
|
-
// ---------------------------------------------------------------------------
|
|
487
|
-
// markTaskTerminalFromWorker — called by gateway when worker reports failure/cancel
|
|
488
|
-
// ---------------------------------------------------------------------------
|
|
489
|
-
markTaskTerminalFromWorker(taskId, targetStatus, errorMessage) {
|
|
490
|
-
const task = this.taskRepo.findById(taskId);
|
|
491
|
-
if (!task)
|
|
492
|
-
return false;
|
|
493
|
-
// Already in target or other terminal status — nothing to do
|
|
494
|
-
if (state_machine_1.TaskStateMachine.isTerminal(task.status))
|
|
495
|
-
return false;
|
|
496
|
-
// Validate transition is legal
|
|
497
|
-
if (!state_machine_1.TaskStateMachine.canTransition(task.status, targetStatus)) {
|
|
498
|
-
this.logger.warn(`Cannot transition task #${taskId} from ${task.status} to ${targetStatus} via worker progress`);
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
const extra = {
|
|
502
|
-
completedAt: new Date().toISOString(),
|
|
503
|
-
};
|
|
504
|
-
if (errorMessage)
|
|
505
|
-
extra.errorMessage = errorMessage;
|
|
506
|
-
const updated = this.updateTaskStatusWithRetry(taskId, targetStatus, task.revision, extra);
|
|
507
|
-
if (updated) {
|
|
508
|
-
// Schedule workspace cleanup
|
|
509
|
-
if (task.machineId) {
|
|
510
|
-
const workspace = this.workspaceRepo.findByTaskId(taskId);
|
|
511
|
-
if (workspace) {
|
|
512
|
-
this.cleanupService
|
|
513
|
-
.scheduleCleanup(taskId, task.machineId, workspace.path)
|
|
514
|
-
.catch((err) => {
|
|
515
|
-
this.logger.error(`Failed to schedule cleanup for task #${taskId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
this.auditLogRepo.create({
|
|
520
|
-
action: `task_${targetStatus.toLowerCase()}_from_worker`,
|
|
521
|
-
resource: `task:${taskId}`,
|
|
522
|
-
detail: `Task marked ${targetStatus} via worker progress frame`,
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
return updated;
|
|
526
|
-
}
|
|
527
|
-
// ---------------------------------------------------------------------------
|
|
528
|
-
// retryTask
|
|
529
|
-
// ---------------------------------------------------------------------------
|
|
530
|
-
async retryTask(taskId, retriedBy) {
|
|
531
|
-
const task = this.taskRepo.findById(taskId);
|
|
532
|
-
if (!task) {
|
|
533
|
-
throw new common_1.NotFoundException(`Task #${taskId} not found`);
|
|
534
|
-
}
|
|
535
|
-
if (task.status !== protocol_1.TaskStatus.FAILED) {
|
|
536
|
-
throw new common_1.BadRequestException(`Cannot retry task #${taskId}: status is ${task.status}, expected FAILED`);
|
|
537
|
-
}
|
|
538
|
-
// Validate transition
|
|
539
|
-
state_machine_1.TaskStateMachine.assertTransition(task.status, protocol_1.TaskStatus.QUEUED);
|
|
540
|
-
// Check fingerprint conflict — another active task with same fingerprint
|
|
541
|
-
if (task.fingerprint) {
|
|
542
|
-
const conflicting = this.taskRepo.findActiveByFingerprint(task.fingerprint);
|
|
543
|
-
if (conflicting && conflicting.id !== taskId) {
|
|
544
|
-
throw new common_1.ConflictException(`Cannot retry: active task #${conflicting.id} has the same fingerprint`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
// Check for existing MR/PR on the branch (if branch exists)
|
|
548
|
-
if (task.branch && task.configSnapshot) {
|
|
549
|
-
const mrCheckResult = await this.checkExistingMergeRequest(task);
|
|
550
|
-
if (mrCheckResult) {
|
|
551
|
-
// Store pending confirm for retry with existing MR
|
|
552
|
-
const confirmKey = `pending_confirm:retry:${crypto.randomUUID()}`;
|
|
553
|
-
const pendingData = JSON.stringify({
|
|
554
|
-
action: 'retry',
|
|
555
|
-
taskId,
|
|
556
|
-
createdBy: retriedBy,
|
|
557
|
-
});
|
|
558
|
-
await this.redis.setex(confirmKey, protocol_1.CONFIRM_PENDING_TTL_SEC, pendingData);
|
|
559
|
-
// Store index from taskId to confirmKey for lookup during confirm
|
|
560
|
-
await this.redis.setex(`pending_confirm_idx:task:${taskId}`, protocol_1.CONFIRM_PENDING_TTL_SEC, confirmKey);
|
|
561
|
-
throw new common_1.ConflictException({
|
|
562
|
-
message: `Task #${taskId} branch '${task.branch}' has an open MR/PR`,
|
|
563
|
-
confirmRequired: {
|
|
564
|
-
reason: 'existing_mr',
|
|
565
|
-
message: `Task #${taskId} branch '${task.branch}' has an open MR/PR (${mrCheckResult}). Retry will force push to the same branch. Continue?`,
|
|
566
|
-
pendingAction: 'retry',
|
|
567
|
-
taskId,
|
|
568
|
-
mrUrl: mrCheckResult,
|
|
569
|
-
confirmKey,
|
|
570
|
-
},
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return this.executeRetry(taskId, retriedBy);
|
|
575
|
-
}
|
|
576
|
-
async executeRetry(taskId, retriedBy) {
|
|
577
|
-
const task = this.taskRepo.findById(taskId);
|
|
578
|
-
if (!task) {
|
|
579
|
-
throw new common_1.NotFoundException(`Task #${taskId} not found`);
|
|
580
|
-
}
|
|
581
|
-
// CAS update: FAILED -> QUEUED
|
|
582
|
-
const updated = this.updateTaskStatusWithRetry(taskId, protocol_1.TaskStatus.QUEUED, task.revision, {
|
|
583
|
-
retryCount: task.retryCount + 1,
|
|
584
|
-
errorMessage: null,
|
|
585
|
-
machineId: null,
|
|
586
|
-
assignedAt: null,
|
|
587
|
-
startedAt: null,
|
|
588
|
-
completedAt: null,
|
|
589
|
-
currentStage: null,
|
|
590
|
-
currentSessionId: null,
|
|
591
|
-
});
|
|
592
|
-
if (!updated) {
|
|
593
|
-
throw new common_1.ConflictException(`Failed to retry task #${taskId}: concurrent modification`);
|
|
594
|
-
}
|
|
595
|
-
// Re-enqueue to BullMQ
|
|
596
|
-
await this.schedulerService.enqueueTask(taskId);
|
|
597
214
|
this.auditLogRepo.create({
|
|
598
|
-
userId:
|
|
599
|
-
action: '
|
|
600
|
-
resource: `
|
|
601
|
-
detail: `
|
|
215
|
+
userId: command.user.id,
|
|
216
|
+
action: drain ? 'worker_drain' : 'worker_undrain',
|
|
217
|
+
resource: `worker:${worker.id}`,
|
|
218
|
+
detail: `Worker '${worker.name}' ${drain ? 'drained' : 'undrained'}`,
|
|
602
219
|
});
|
|
603
|
-
|
|
604
|
-
return this.taskRepo.findById(taskId);
|
|
605
|
-
}
|
|
606
|
-
// ---------------------------------------------------------------------------
|
|
607
|
-
// confirmStage — forward interactive stage confirm result to Worker
|
|
608
|
-
// ---------------------------------------------------------------------------
|
|
609
|
-
async confirmStage(taskId, stageIndex, result) {
|
|
610
|
-
const task = this.taskRepo.findById(taskId);
|
|
611
|
-
if (!task) {
|
|
612
|
-
throw new common_1.NotFoundException(`Task #${taskId} not found`);
|
|
613
|
-
}
|
|
614
|
-
if (task.status !== protocol_1.TaskStatus.RUNNING &&
|
|
615
|
-
task.status !== protocol_1.TaskStatus.SUSPENDED) {
|
|
616
|
-
throw new common_1.BadRequestException(`Task #${taskId} is not in a confirmable state (${task.status})`);
|
|
617
|
-
}
|
|
618
|
-
if (!task.machineId) {
|
|
619
|
-
throw new common_1.BadRequestException(`Task #${taskId} has no assigned machine`);
|
|
620
|
-
}
|
|
621
|
-
// Forward confirm response to Worker via WS
|
|
622
|
-
const frame = {
|
|
623
|
-
type: 'stage_confirm_response',
|
|
624
|
-
taskId,
|
|
625
|
-
stageIndex,
|
|
626
|
-
result,
|
|
627
|
-
};
|
|
628
|
-
const ws = this.workerConnectionManager.getConnection(task.machineId);
|
|
629
|
-
if (!ws || ws.readyState !== 1 /* WebSocket.OPEN */) {
|
|
630
|
-
throw new common_1.BadRequestException(`Worker for task #${taskId} is not connected`);
|
|
631
|
-
}
|
|
632
|
-
ws.send(JSON.stringify(frame));
|
|
633
|
-
// Set a short TTL on the stage confirm key instead of deleting immediately.
|
|
634
|
-
// This gives a 60-second window for retry if the worker doesn't process the message,
|
|
635
|
-
// while still ensuring eventual cleanup.
|
|
636
|
-
const stageConfirmKey = `confirm:stage:${taskId}:${stageIndex}`;
|
|
637
|
-
await this.redis.expire(stageConfirmKey, 60);
|
|
638
|
-
this.logger.log(`Stage confirm forwarded to Worker for task #${taskId}, stage ${stageIndex}`);
|
|
220
|
+
return { success: true, message: `Worker '${worker.name}' ${drain ? 'drained' : 'undrained'}` };
|
|
639
221
|
}
|
|
640
222
|
// ---------------------------------------------------------------------------
|
|
641
|
-
//
|
|
223
|
+
// Task query methods
|
|
642
224
|
// ---------------------------------------------------------------------------
|
|
643
225
|
getTask(taskId) {
|
|
644
226
|
return this.taskRepo.findById(taskId);
|
|
@@ -652,477 +234,17 @@ let DispatcherService = DispatcherService_1 = class DispatcherService {
|
|
|
652
234
|
listVisibleTasks(developerId, query) {
|
|
653
235
|
return this.taskRepo.listVisible(developerId, query);
|
|
654
236
|
}
|
|
655
|
-
// ---------------------------------------------------------------------------
|
|
656
|
-
// registerWorker
|
|
657
|
-
// ---------------------------------------------------------------------------
|
|
658
|
-
async registerWorker(request) {
|
|
659
|
-
// 1. Parse token format: ovw_<id>_<secret>
|
|
660
|
-
const { tokenId, secret } = this.parseWorkerToken(request.token);
|
|
661
|
-
// 2. Find token by id, verify it's active and unused
|
|
662
|
-
const token = this.workerTokenRepo.findActiveUnused(tokenId);
|
|
663
|
-
if (!token) {
|
|
664
|
-
throw new common_1.UnauthorizedException('Invalid or already used worker token');
|
|
665
|
-
}
|
|
666
|
-
// 3. Verify bcrypt(secret, hash)
|
|
667
|
-
const secretValid = await this.cryptoService.comparePassword(secret, token.tokenHash);
|
|
668
|
-
if (!secretValid) {
|
|
669
|
-
throw new common_1.UnauthorizedException('Invalid worker token secret');
|
|
670
|
-
}
|
|
671
|
-
// 4. Validate protocol version (major must match)
|
|
672
|
-
this.validateProtocolVersion(request.protocolVersion);
|
|
673
|
-
// 5. Resolve machine: by explicit ID, by name, or create new
|
|
674
|
-
let machineId = request.machineId;
|
|
675
|
-
let machine = machineId ? this.machineRepo.findById(machineId) : null;
|
|
676
|
-
let isFirstRegistration = !machineId;
|
|
677
|
-
// If no explicit machineId, check if a machine with this name already exists
|
|
678
|
-
if (!machine && !machineId) {
|
|
679
|
-
const existingByName = this.machineRepo.findByName(request.machineName);
|
|
680
|
-
if (existingByName) {
|
|
681
|
-
if (existingByName.status === protocol_1.MachineStatus.DECOMMISSIONED) {
|
|
682
|
-
// Archive the decommissioned machine by renaming it, then create fresh
|
|
683
|
-
const archiveName = `${existingByName.name}_decom_${existingByName.id.slice(0, 8)}`;
|
|
684
|
-
this.machineRepo.updateName(existingByName.id, archiveName);
|
|
685
|
-
this.logger.log(`Archived decommissioned machine '${existingByName.name}' → '${archiveName}'`);
|
|
686
|
-
}
|
|
687
|
-
else {
|
|
688
|
-
machine = existingByName;
|
|
689
|
-
machineId = existingByName.id;
|
|
690
|
-
isFirstRegistration = false;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
if (!machineId) {
|
|
695
|
-
machineId = crypto.randomUUID();
|
|
696
|
-
}
|
|
697
|
-
// Reject re-registration of decommissioned machines (by explicit machineId)
|
|
698
|
-
if (machine && machine.status === protocol_1.MachineStatus.DECOMMISSIONED) {
|
|
699
|
-
throw new common_1.ForbiddenException(`Machine '${machineId}' has been decommissioned and cannot re-register`);
|
|
700
|
-
}
|
|
701
|
-
// 6. All validations passed — now mark token as used and mutate state
|
|
702
|
-
this.workerTokenRepo.markUsed(tokenId);
|
|
703
|
-
if (machine) {
|
|
704
|
-
// Update existing machine
|
|
705
|
-
this.machineRepo.updateStatus(machineId, protocol_1.MachineStatus.ONLINE);
|
|
706
|
-
this.machineRepo.updateTokenId(machineId, tokenId);
|
|
707
|
-
}
|
|
708
|
-
else {
|
|
709
|
-
// Create new machine
|
|
710
|
-
machine = this.machineRepo.create({
|
|
711
|
-
id: machineId,
|
|
712
|
-
name: request.machineName,
|
|
713
|
-
host: request.host,
|
|
714
|
-
port: request.port,
|
|
715
|
-
tokenId,
|
|
716
|
-
os: request.os,
|
|
717
|
-
cpuModel: request.cpuModel,
|
|
718
|
-
cpuCores: request.cpuCores,
|
|
719
|
-
memoryGb: request.memoryGb,
|
|
720
|
-
capabilities: request.capabilities,
|
|
721
|
-
tags: request.tags,
|
|
722
|
-
protocolVersion: request.protocolVersion,
|
|
723
|
-
});
|
|
724
|
-
}
|
|
725
|
-
// 7. Generate recovery secret on first registration
|
|
726
|
-
let recoverySecret;
|
|
727
|
-
if (isFirstRegistration) {
|
|
728
|
-
recoverySecret = crypto.randomBytes(32).toString('hex');
|
|
729
|
-
const recoveryHash = await this.cryptoService.hashPassword(recoverySecret);
|
|
730
|
-
this.machineRepo.updateRecoverySecretHash(machineId, recoveryHash);
|
|
731
|
-
}
|
|
732
|
-
// 8. Sign Worker JWT
|
|
733
|
-
const jwt = this.authService.signWorkerJwt(machineId, tokenId);
|
|
734
|
-
this.auditLogRepo.create({
|
|
735
|
-
action: 'worker_register',
|
|
736
|
-
resource: `machine:${machineId}`,
|
|
737
|
-
detail: `Worker '${request.machineName}' registered (token #${tokenId}, first=${isFirstRegistration})`,
|
|
738
|
-
});
|
|
739
|
-
this.logger.log(`Worker '${request.machineName}' registered as ${machineId}`);
|
|
740
|
-
return {
|
|
741
|
-
jwt,
|
|
742
|
-
machineId,
|
|
743
|
-
recoverySecret,
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
// ---------------------------------------------------------------------------
|
|
747
|
-
// refreshWorkerToken
|
|
748
|
-
// ---------------------------------------------------------------------------
|
|
749
|
-
async refreshWorkerToken(request, currentJwt) {
|
|
750
|
-
const { machineId } = request;
|
|
751
|
-
// Verify tokenId matches the current JWT's tokenId
|
|
752
|
-
const machine = this.machineRepo.findById(machineId);
|
|
753
|
-
if (!machine) {
|
|
754
|
-
throw new common_1.NotFoundException(`Machine '${machineId}' not found`);
|
|
755
|
-
}
|
|
756
|
-
if (machine.tokenId !== currentJwt.tokenId) {
|
|
757
|
-
throw new common_1.UnauthorizedException('Token ID mismatch — token may have been revoked');
|
|
758
|
-
}
|
|
759
|
-
// Verify the token hasn't been revoked
|
|
760
|
-
const token = this.workerTokenRepo.findById(currentJwt.tokenId);
|
|
761
|
-
if (!token || token.status === 'revoked') {
|
|
762
|
-
throw new common_1.UnauthorizedException('Worker token has been revoked');
|
|
763
|
-
}
|
|
764
|
-
// Sign new Worker JWT
|
|
765
|
-
const jwt = this.authService.signWorkerJwt(machineId, currentJwt.tokenId);
|
|
766
|
-
this.logger.debug(`Worker JWT refreshed for machine ${machineId}`);
|
|
767
|
-
return { jwt };
|
|
768
|
-
}
|
|
769
|
-
// ---------------------------------------------------------------------------
|
|
770
|
-
// recoverWorker
|
|
771
|
-
// ---------------------------------------------------------------------------
|
|
772
|
-
async recoverWorker(request) {
|
|
773
|
-
const { machineId, machineName, recoverySecret } = request;
|
|
774
|
-
// 1. Find machine
|
|
775
|
-
const machine = this.machineRepo.findById(machineId);
|
|
776
|
-
if (!machine) {
|
|
777
|
-
throw new common_1.NotFoundException(`Machine '${machineId}' not found`);
|
|
778
|
-
}
|
|
779
|
-
// 2. Reject recovery of decommissioned machines
|
|
780
|
-
if (machine.status === protocol_1.MachineStatus.DECOMMISSIONED) {
|
|
781
|
-
throw new common_1.ForbiddenException(`Machine '${machineId}' has been decommissioned and cannot recover`);
|
|
782
|
-
}
|
|
783
|
-
// 3. Validate machine name matches
|
|
784
|
-
if (machine.name !== machineName) {
|
|
785
|
-
throw new common_1.UnauthorizedException('Machine name does not match the registered name');
|
|
786
|
-
}
|
|
787
|
-
// 3. Validate recovery secret hash exists
|
|
788
|
-
if (!machine.recoverySecretHash) {
|
|
789
|
-
throw new common_1.UnauthorizedException('No recovery secret configured for this machine');
|
|
790
|
-
}
|
|
791
|
-
// 4. Validate recovery secret
|
|
792
|
-
const secretValid = await this.cryptoService.comparePassword(recoverySecret, machine.recoverySecretHash);
|
|
793
|
-
if (!secretValid) {
|
|
794
|
-
throw new common_1.UnauthorizedException('Invalid recovery secret');
|
|
795
|
-
}
|
|
796
|
-
// 5. Atomically claim the recovery token via Redis GETDEL to prevent
|
|
797
|
-
// race conditions where two concurrent recovery requests consume
|
|
798
|
-
// the same token.
|
|
799
|
-
const recoveryClaimKey = `recovery_claim:${machineId}`;
|
|
800
|
-
// Set the claim key if not already present (first recovery wins)
|
|
801
|
-
const claimed = await this.redis.getClient().set(recoveryClaimKey, '1', 'EX', 30, 'NX');
|
|
802
|
-
if (claimed === null) {
|
|
803
|
-
throw new common_1.ConflictException('Recovery already in progress for this machine');
|
|
804
|
-
}
|
|
805
|
-
try {
|
|
806
|
-
// 6. Wrap SQLite updates in a transaction for atomicity
|
|
807
|
-
const db = this.databaseService.getDb();
|
|
808
|
-
const runRecoveryTransaction = db.transaction(() => {
|
|
809
|
-
// Update machine status to online
|
|
810
|
-
db.prepare('UPDATE machines SET status = ? WHERE id = ?')
|
|
811
|
-
.run(protocol_1.MachineStatus.ONLINE, machineId);
|
|
812
|
-
// Record audit log
|
|
813
|
-
db.prepare(`INSERT INTO audit_logs (user_id, action, resource, detail)
|
|
814
|
-
VALUES (?, ?, ?, ?)`).run(null, 'worker_recover', `machine:${machineId}`, `Worker '${machineName}' recovered via recovery secret`);
|
|
815
|
-
});
|
|
816
|
-
runRecoveryTransaction();
|
|
817
|
-
// 7. Issue new token — sign Worker JWT with existing tokenId
|
|
818
|
-
const jwt = this.authService.signWorkerJwt(machineId, machine.tokenId);
|
|
819
|
-
this.logger.log(`Worker '${machineName}' (${machineId}) recovered via recovery secret`);
|
|
820
|
-
return { jwt, machineId };
|
|
821
|
-
}
|
|
822
|
-
finally {
|
|
823
|
-
// Clean up the claim key
|
|
824
|
-
await this.redis.del(recoveryClaimKey);
|
|
825
|
-
}
|
|
826
|
-
}
|
|
827
|
-
// ---------------------------------------------------------------------------
|
|
828
|
-
// issuePtyToken
|
|
829
|
-
// ---------------------------------------------------------------------------
|
|
830
|
-
issuePtyToken(taskId, aud) {
|
|
831
|
-
const task = this.taskRepo.findById(taskId);
|
|
832
|
-
if (!task) {
|
|
833
|
-
throw new common_1.NotFoundException(`Task #${taskId} not found`);
|
|
834
|
-
}
|
|
835
|
-
return this.authService.signChannelToken(taskId, aud);
|
|
836
|
-
}
|
|
837
|
-
// ---------------------------------------------------------------------------
|
|
838
|
-
// Helpers
|
|
839
|
-
// ---------------------------------------------------------------------------
|
|
840
|
-
resolveProject(keyOrAlias) {
|
|
841
|
-
if (!keyOrAlias) {
|
|
842
|
-
// Try default project
|
|
843
|
-
return this.projectRepo.findDefault();
|
|
844
|
-
}
|
|
845
|
-
// Try by key first
|
|
846
|
-
const byKey = this.projectRepo.findByKey(keyOrAlias);
|
|
847
|
-
if (byKey)
|
|
848
|
-
return byKey;
|
|
849
|
-
// Try by alias
|
|
850
|
-
return this.projectRepo.findByAlias(keyOrAlias);
|
|
851
|
-
}
|
|
852
|
-
parseWorkerToken(raw) {
|
|
853
|
-
// Format: ovw_<id>_<secret>
|
|
854
|
-
const match = /^ovw_(\d+)_(.+)$/.exec(raw);
|
|
855
|
-
if (!match) {
|
|
856
|
-
throw new common_1.UnauthorizedException('Invalid token format. Expected: ovw_<id>_<secret>');
|
|
857
|
-
}
|
|
858
|
-
return {
|
|
859
|
-
tokenId: parseInt(match[1], 10),
|
|
860
|
-
secret: match[2],
|
|
861
|
-
};
|
|
862
|
-
}
|
|
863
|
-
validateProtocolVersion(workerVersion) {
|
|
864
|
-
const [serverMajor] = protocol_1.PROTOCOL_VERSION.split('.');
|
|
865
|
-
const [workerMajor] = workerVersion.split('.');
|
|
866
|
-
if (serverMajor !== workerMajor) {
|
|
867
|
-
throw new common_1.BadRequestException(`Protocol version mismatch: server=${protocol_1.PROTOCOL_VERSION}, worker=${workerVersion}. Major version must match.`);
|
|
868
|
-
}
|
|
869
|
-
if (workerVersion !== protocol_1.PROTOCOL_VERSION) {
|
|
870
|
-
this.logger.warn(`Protocol minor version mismatch: server=${protocol_1.PROTOCOL_VERSION}, worker=${workerVersion}`);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Resolve a pending confirmation from Redis.
|
|
875
|
-
* Tries reply-to msgId lookup first, then taskId-based index.
|
|
876
|
-
* Validates that the confirming user matches the original creator.
|
|
877
|
-
* Consumes (deletes) the pending confirm on success.
|
|
878
|
-
*/
|
|
879
|
-
async resolvePendingConfirm(taskId, userId, _msgId) {
|
|
880
|
-
// Look up the confirm key via taskId index (non-destructive read)
|
|
881
|
-
const indexKey = `pending_confirm_idx:task:${taskId}`;
|
|
882
|
-
const confirmKey = await this.redis.get(indexKey);
|
|
883
|
-
if (!confirmKey)
|
|
884
|
-
return null;
|
|
885
|
-
// Atomically consume the confirm data (GETDEL prevents double-confirm race)
|
|
886
|
-
const raw = await this.redis.getdel(confirmKey);
|
|
887
|
-
if (!raw)
|
|
888
|
-
return null;
|
|
889
|
-
let data;
|
|
890
|
-
try {
|
|
891
|
-
data = JSON.parse(raw);
|
|
892
|
-
}
|
|
893
|
-
catch {
|
|
894
|
-
return null;
|
|
895
|
-
}
|
|
896
|
-
// Validate user matches original creator.
|
|
897
|
-
// If auth fails the confirm data is already consumed (intentional —
|
|
898
|
-
// prevents an unauthorized user from probing indefinitely, and the
|
|
899
|
-
// confirm would have expired via TTL anyway).
|
|
900
|
-
if (data.createdBy !== userId) {
|
|
901
|
-
this.logger.warn(`Confirm attempt by user #${userId} but pending confirm belongs to user #${data.createdBy}`);
|
|
902
|
-
return null;
|
|
903
|
-
}
|
|
904
|
-
// Clean up the index key
|
|
905
|
-
await this.redis.del(indexKey);
|
|
906
|
-
return data;
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Update task status with CAS retry (up to CAS_MAX_RETRIES).
|
|
910
|
-
* Returns true if successful, throws ConflictException on exhaustion.
|
|
911
|
-
*/
|
|
912
|
-
updateTaskStatusWithRetry(taskId, status, initialRevision, extra) {
|
|
913
|
-
let revision = initialRevision;
|
|
914
|
-
for (let attempt = 0; attempt < protocol_1.CAS_MAX_RETRIES; attempt++) {
|
|
915
|
-
const success = this.taskRepo.updateStatus(taskId, status, revision, extra);
|
|
916
|
-
if (success)
|
|
917
|
-
return true;
|
|
918
|
-
// Reload task to get fresh revision
|
|
919
|
-
const freshTask = this.taskRepo.findById(taskId);
|
|
920
|
-
if (!freshTask) {
|
|
921
|
-
throw new common_1.NotFoundException(`Task #${taskId} not found during CAS retry`);
|
|
922
|
-
}
|
|
923
|
-
// If task already reached target status, that's fine
|
|
924
|
-
if (freshTask.status === status)
|
|
925
|
-
return true;
|
|
926
|
-
revision = freshTask.revision;
|
|
927
|
-
}
|
|
928
|
-
throw new common_1.ConflictException(`CAS retry exhausted for task #${taskId} after ${protocol_1.CAS_MAX_RETRIES} attempts`);
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* Send cancel frame to Worker and wait for ack within CANCEL_ACK_TIMEOUT_MS.
|
|
932
|
-
*/
|
|
933
|
-
async sendCancelToWorkerWithAck(machineId, taskId) {
|
|
934
|
-
const msgId = crypto.randomUUID();
|
|
935
|
-
try {
|
|
936
|
-
await this.workerConnectionManager
|
|
937
|
-
.sendWithAck(machineId, { type: 'cancel', msgId, taskId }, protocol_1.CANCEL_ACK_TIMEOUT_MS);
|
|
938
|
-
return true;
|
|
939
|
-
}
|
|
940
|
-
catch {
|
|
941
|
-
return false;
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
/**
|
|
945
|
-
* Send cancel frame to Worker (fire and forget).
|
|
946
|
-
*/
|
|
947
|
-
sendCancelToWorker(machineId, taskId) {
|
|
948
|
-
const msgId = crypto.randomUUID();
|
|
949
|
-
this.workerConnectionManager.send(machineId, {
|
|
950
|
-
type: 'cancel',
|
|
951
|
-
msgId,
|
|
952
|
-
taskId,
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* Check if the task's branch has an existing open MR/PR on the Git platform.
|
|
957
|
-
* Returns the MR/PR URL if found, null otherwise.
|
|
958
|
-
* On timeout or error, returns null (allows retry to proceed).
|
|
959
|
-
*
|
|
960
|
-
* Supports GitLab and GitHub APIs. Extracts the project path from the repo URL
|
|
961
|
-
* and queries the platform API for open merge/pull requests on the branch.
|
|
962
|
-
*/
|
|
963
|
-
async checkExistingMergeRequest(task) {
|
|
964
|
-
if (!task.branch || !task.configSnapshot)
|
|
965
|
-
return null;
|
|
966
|
-
try {
|
|
967
|
-
const config = JSON.parse(task.configSnapshot);
|
|
968
|
-
const { repoUrl, gitPlatform } = config;
|
|
969
|
-
if (!repoUrl)
|
|
970
|
-
return null;
|
|
971
|
-
// Extract the project path from the repo URL.
|
|
972
|
-
const projectPath = this.extractProjectPath(repoUrl);
|
|
973
|
-
if (!projectPath) {
|
|
974
|
-
this.logger.warn(`Cannot extract project path from repo URL '${repoUrl}' for task #${task.id}`);
|
|
975
|
-
return task.mrUrl ?? null;
|
|
976
|
-
}
|
|
977
|
-
// Extract the base URL for API calls
|
|
978
|
-
const baseUrl = this.extractGitBaseUrl(repoUrl);
|
|
979
|
-
if (!baseUrl) {
|
|
980
|
-
this.logger.warn(`Cannot extract base URL from repo URL '${repoUrl}' for task #${task.id}`);
|
|
981
|
-
return task.mrUrl ?? null;
|
|
982
|
-
}
|
|
983
|
-
// Look up the git token. Since the DB schema does not currently include
|
|
984
|
-
// git_token_encrypted, use the GIT_TOKEN environment variable as fallback.
|
|
985
|
-
const gitToken = process.env.GIT_TOKEN || '';
|
|
986
|
-
if (!gitToken) {
|
|
987
|
-
this.logger.debug(`No GIT_TOKEN configured; skipping MR/PR API check for task #${task.id}`);
|
|
988
|
-
return task.mrUrl ?? null;
|
|
989
|
-
}
|
|
990
|
-
const abortController = new AbortController();
|
|
991
|
-
const timeout = setTimeout(() => abortController.abort(), 10_000);
|
|
992
|
-
try {
|
|
993
|
-
let mrUrl = null;
|
|
994
|
-
if (gitPlatform === 'gitlab') {
|
|
995
|
-
mrUrl = await this.checkGitLabMergeRequest(baseUrl, projectPath, task.branch, gitToken, abortController.signal);
|
|
996
|
-
}
|
|
997
|
-
else if (gitPlatform === 'github') {
|
|
998
|
-
mrUrl = await this.checkGitHubPullRequest(baseUrl, projectPath, task.branch, gitToken, abortController.signal);
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
this.logger.debug(`Unsupported git platform '${gitPlatform}' for MR/PR check on task #${task.id}`);
|
|
1002
|
-
return task.mrUrl ?? null;
|
|
1003
|
-
}
|
|
1004
|
-
return mrUrl;
|
|
1005
|
-
}
|
|
1006
|
-
finally {
|
|
1007
|
-
clearTimeout(timeout);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
catch (err) {
|
|
1011
|
-
this.logger.warn(`MR/PR check failed for task #${task.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1012
|
-
// On error, allow retry to proceed
|
|
1013
|
-
return null;
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
/**
|
|
1017
|
-
* Query GitLab API for open merge requests on a branch.
|
|
1018
|
-
* GET /api/v4/projects/:id/merge_requests?source_branch=...&state=opened
|
|
1019
|
-
*/
|
|
1020
|
-
async checkGitLabMergeRequest(baseUrl, projectPath, branch, token, signal) {
|
|
1021
|
-
const encodedPath = encodeURIComponent(projectPath);
|
|
1022
|
-
const url = `${baseUrl}/api/v4/projects/${encodedPath}/merge_requests?source_branch=${encodeURIComponent(branch)}&state=opened&per_page=1`;
|
|
1023
|
-
const response = await fetch(url, {
|
|
1024
|
-
headers: {
|
|
1025
|
-
'PRIVATE-TOKEN': token,
|
|
1026
|
-
'Accept': 'application/json',
|
|
1027
|
-
},
|
|
1028
|
-
signal,
|
|
1029
|
-
});
|
|
1030
|
-
if (!response.ok) {
|
|
1031
|
-
this.logger.warn(`GitLab MR check returned ${response.status} for project '${projectPath}', branch '${branch}'`);
|
|
1032
|
-
return null;
|
|
1033
|
-
}
|
|
1034
|
-
const data = (await response.json());
|
|
1035
|
-
if (Array.isArray(data) && data.length > 0 && data[0].web_url) {
|
|
1036
|
-
this.logger.debug(`Found open GitLab MR for branch '${branch}': ${data[0].web_url}`);
|
|
1037
|
-
return data[0].web_url;
|
|
1038
|
-
}
|
|
1039
|
-
return null;
|
|
1040
|
-
}
|
|
1041
|
-
/**
|
|
1042
|
-
* Query GitHub API for open pull requests on a branch.
|
|
1043
|
-
* GET /repos/:owner/:repo/pulls?head=owner:branch&state=open
|
|
1044
|
-
*/
|
|
1045
|
-
async checkGitHubPullRequest(baseUrl, projectPath, branch, token, signal) {
|
|
1046
|
-
const owner = projectPath.split('/')[0];
|
|
1047
|
-
const apiBase = baseUrl === 'https://github.com'
|
|
1048
|
-
? 'https://api.github.com'
|
|
1049
|
-
: `${baseUrl}/api/v3`;
|
|
1050
|
-
const url = `${apiBase}/repos/${projectPath}/pulls?head=${encodeURIComponent(`${owner}:${branch}`)}&state=open&per_page=1`;
|
|
1051
|
-
const response = await fetch(url, {
|
|
1052
|
-
headers: {
|
|
1053
|
-
'Authorization': `Bearer ${token}`,
|
|
1054
|
-
'Accept': 'application/vnd.github+json',
|
|
1055
|
-
'X-GitHub-Api-Version': '2022-11-28',
|
|
1056
|
-
},
|
|
1057
|
-
signal,
|
|
1058
|
-
});
|
|
1059
|
-
if (!response.ok) {
|
|
1060
|
-
this.logger.warn(`GitHub PR check returned ${response.status} for project '${projectPath}', branch '${branch}'`);
|
|
1061
|
-
return null;
|
|
1062
|
-
}
|
|
1063
|
-
const data = (await response.json());
|
|
1064
|
-
if (Array.isArray(data) && data.length > 0 && data[0].html_url) {
|
|
1065
|
-
this.logger.debug(`Found open GitHub PR for branch '${branch}': ${data[0].html_url}`);
|
|
1066
|
-
return data[0].html_url;
|
|
1067
|
-
}
|
|
1068
|
-
return null;
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* Extract the project path (e.g. "group/project") from a git repo URL.
|
|
1072
|
-
* Supports HTTPS and SSH formats.
|
|
1073
|
-
*/
|
|
1074
|
-
extractProjectPath(repoUrl) {
|
|
1075
|
-
// SSH format: git@host:path.git
|
|
1076
|
-
const sshMatch = /^git@[^:]+:(.+?)(?:\.git)?$/.exec(repoUrl);
|
|
1077
|
-
if (sshMatch)
|
|
1078
|
-
return sshMatch[1];
|
|
1079
|
-
// HTTPS format: https://host/path.git or https://host/path
|
|
1080
|
-
try {
|
|
1081
|
-
const parsed = new URL(repoUrl);
|
|
1082
|
-
let path = parsed.pathname;
|
|
1083
|
-
path = path.replace(/^\//, '').replace(/\.git$/, '');
|
|
1084
|
-
return path || null;
|
|
1085
|
-
}
|
|
1086
|
-
catch {
|
|
1087
|
-
return null;
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Extract the base URL (scheme + host) from a git repo URL.
|
|
1092
|
-
*/
|
|
1093
|
-
extractGitBaseUrl(repoUrl) {
|
|
1094
|
-
// SSH format: git@host:path -> https://host
|
|
1095
|
-
const sshMatch = /^git@([^:]+):/.exec(repoUrl);
|
|
1096
|
-
if (sshMatch)
|
|
1097
|
-
return `https://${sshMatch[1]}`;
|
|
1098
|
-
// HTTPS format
|
|
1099
|
-
try {
|
|
1100
|
-
const parsed = new URL(repoUrl);
|
|
1101
|
-
return `${parsed.protocol}//${parsed.host}`;
|
|
1102
|
-
}
|
|
1103
|
-
catch {
|
|
1104
|
-
return null;
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
237
|
};
|
|
1108
238
|
exports.DispatcherService = DispatcherService;
|
|
1109
239
|
exports.DispatcherService = DispatcherService = DispatcherService_1 = __decorate([
|
|
1110
240
|
(0, common_1.Injectable)(),
|
|
1111
241
|
__metadata("design:paramtypes", [task_repository_1.TaskRepository,
|
|
1112
|
-
|
|
1113
|
-
project_repository_1.ProjectRepository,
|
|
1114
|
-
project_member_repository_1.ProjectMemberRepository,
|
|
1115
|
-
worker_token_repository_1.WorkerTokenRepository,
|
|
242
|
+
worker_repository_1.WorkerRepository,
|
|
1116
243
|
audit_log_repository_1.AuditLogRepository,
|
|
1117
244
|
developer_repository_1.DeveloperRepository,
|
|
1118
|
-
database_service_1.DatabaseService,
|
|
1119
|
-
auth_service_1.AuthService,
|
|
1120
|
-
crypto_service_1.CryptoService,
|
|
1121
|
-
redis_service_1.RedisService,
|
|
1122
245
|
dedup_service_1.DedupService,
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
workspace_repository_1.WorkspaceRepository])
|
|
246
|
+
task_creation_service_1.TaskCreationService,
|
|
247
|
+
task_lifecycle_service_1.TaskLifecycleService,
|
|
248
|
+
event_emitter_1.EventEmitter2])
|
|
1127
249
|
], DispatcherService);
|
|
1128
250
|
//# sourceMappingURL=dispatcher.service.js.map
|