devchain-cli 0.9.2 → 0.10.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/cli.js +985 -194
- package/dist/drizzle/0044_supreme_joshua_kane.sql +57 -0
- package/dist/drizzle/0045_provider_auto_compact_threshold.sql +11 -0
- package/dist/drizzle/0046_worktrees_owner_project_id.sql +3 -0
- package/dist/drizzle/meta/0044_snapshot.json +4620 -0
- package/dist/drizzle/meta/_journal.json +22 -1
- package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts +18 -0
- package/dist/node_modules/@devchain/shared/schemas/export-schema.d.ts.map +1 -1
- package/dist/node_modules/@devchain/shared/schemas/export-schema.js +6 -0
- package/dist/node_modules/@devchain/shared/schemas/export-schema.js.map +1 -1
- package/dist/node_modules/@devchain/shared/tsconfig.tsbuildinfo +1 -1
- package/dist/server/app.main.module.d.ts +4 -0
- package/dist/server/app.main.module.js +102 -0
- package/dist/server/app.main.module.js.map +1 -0
- package/dist/server/app.module.d.ts +1 -4
- package/dist/server/app.module.js +2 -78
- package/dist/server/app.module.js.map +1 -1
- package/dist/server/app.normal.module.d.ts +4 -0
- package/dist/server/app.normal.module.js +90 -0
- package/dist/server/app.normal.module.js.map +1 -0
- package/dist/server/common/config/env.config.d.ts +81 -1
- package/dist/server/common/config/env.config.js +52 -2
- package/dist/server/common/config/env.config.js.map +1 -1
- package/dist/server/common/templates-directory.d.ts +8 -0
- package/dist/server/common/templates-directory.js +56 -0
- package/dist/server/common/templates-directory.js.map +1 -0
- package/dist/server/main.js +58 -7
- package/dist/server/main.js.map +1 -1
- package/dist/server/modules/chat/dtos/chat.dto.d.ts +2 -2
- package/dist/server/modules/core/controllers/health.controller.d.ts +4 -0
- package/dist/server/modules/core/controllers/health.controller.js +22 -1
- package/dist/server/modules/core/controllers/health.controller.js.map +1 -1
- package/dist/server/modules/core/controllers/runtime.controller.d.ts +18 -0
- package/dist/server/modules/core/controllers/runtime.controller.js +130 -0
- package/dist/server/modules/core/controllers/runtime.controller.js.map +1 -0
- package/dist/server/modules/core/core-common.module.d.ts +2 -0
- package/dist/server/modules/core/core-common.module.js +24 -0
- package/dist/server/modules/core/core-common.module.js.map +1 -0
- package/dist/server/modules/core/core-main-health.module.d.ts +2 -0
- package/dist/server/modules/core/core-main-health.module.js +32 -0
- package/dist/server/modules/core/core-main-health.module.js.map +1 -0
- package/dist/server/modules/core/core-normal-health.module.d.ts +2 -0
- package/dist/server/modules/core/core-normal-health.module.js +29 -0
- package/dist/server/modules/core/core-normal-health.module.js.map +1 -0
- package/dist/server/modules/core/core-normal.module.d.ts +2 -0
- package/dist/server/modules/core/core-normal.module.js +28 -0
- package/dist/server/modules/core/core-normal.module.js.map +1 -0
- package/dist/server/modules/core/core.module.js +4 -11
- package/dist/server/modules/core/core.module.js.map +1 -1
- package/dist/server/modules/core/services/health.service.d.ts +13 -0
- package/dist/server/modules/core/services/health.service.js +39 -0
- package/dist/server/modules/core/services/health.service.js.map +1 -0
- package/dist/server/modules/core/services/main-readiness-checker.service.d.ts +14 -0
- package/dist/server/modules/core/services/main-readiness-checker.service.js +82 -0
- package/dist/server/modules/core/services/main-readiness-checker.service.js.map +1 -0
- package/dist/server/modules/core/services/normal-readiness-checker.service.d.ts +13 -0
- package/dist/server/modules/core/services/normal-readiness-checker.service.js +67 -0
- package/dist/server/modules/core/services/normal-readiness-checker.service.js.map +1 -0
- package/dist/server/modules/core/services/preflight.service.d.ts +1 -0
- package/dist/server/modules/core/services/preflight.service.js +18 -1
- package/dist/server/modules/core/services/preflight.service.js.map +1 -1
- package/dist/server/modules/core/services/provider-mcp-ensure.service.js +8 -0
- package/dist/server/modules/core/services/provider-mcp-ensure.service.js.map +1 -1
- package/dist/server/modules/epics/epics.module.js +2 -2
- package/dist/server/modules/epics/epics.module.js.map +1 -1
- package/dist/server/modules/events/catalog/claude.hooks.session.started.d.ts +39 -0
- package/dist/server/modules/events/catalog/claude.hooks.session.started.js +20 -0
- package/dist/server/modules/events/catalog/claude.hooks.session.started.js.map +1 -0
- package/dist/server/modules/events/catalog/epic.created.d.ts +2 -2
- package/dist/server/modules/events/catalog/index.d.ts +38 -4
- package/dist/server/modules/events/catalog/index.js +2 -0
- package/dist/server/modules/events/catalog/index.js.map +1 -1
- package/dist/server/modules/events/catalog/terminal.watcher.triggered.d.ts +2 -2
- package/dist/server/modules/events/controllers/event-log.controller.d.ts +1 -1
- package/dist/server/modules/events/controllers/event-log.controller.js +11 -9
- package/dist/server/modules/events/controllers/event-log.controller.js.map +1 -1
- package/dist/server/modules/events/dtos/event-log.dto.d.ts +1 -0
- package/dist/server/modules/events/events-domain.module.d.ts +2 -0
- package/dist/server/modules/events/events-domain.module.js +42 -0
- package/dist/server/modules/events/events-domain.module.js.map +1 -0
- package/dist/server/modules/events/events-infra.module.d.ts +2 -0
- package/dist/server/modules/events/events-infra.module.js +26 -0
- package/dist/server/modules/events/events-infra.module.js.map +1 -0
- package/dist/server/modules/events/events.module.js +4 -27
- package/dist/server/modules/events/events.module.js.map +1 -1
- package/dist/server/modules/events/index.d.ts +2 -0
- package/dist/server/modules/events/index.js +2 -0
- package/dist/server/modules/events/index.js.map +1 -1
- package/dist/server/modules/events/services/event-log.service.d.ts +8 -1
- package/dist/server/modules/events/services/event-log.service.js +41 -0
- package/dist/server/modules/events/services/event-log.service.js.map +1 -1
- package/dist/server/modules/events/subscribers/index.js +2 -0
- package/dist/server/modules/events/subscribers/index.js.map +1 -1
- package/dist/server/modules/events/subscribers/worktree-broadcaster.subscriber.d.ts +8 -0
- package/dist/server/modules/events/subscribers/worktree-broadcaster.subscriber.js +48 -0
- package/dist/server/modules/events/subscribers/worktree-broadcaster.subscriber.js.map +1 -0
- package/dist/server/modules/git/dtos/git.dto.d.ts +1 -1
- package/dist/server/modules/guests/guests.module.js +2 -2
- package/dist/server/modules/guests/guests.module.js.map +1 -1
- package/dist/server/modules/hooks/controllers/hooks.controller.d.ts +7 -0
- package/dist/server/modules/hooks/controllers/hooks.controller.js +49 -0
- package/dist/server/modules/hooks/controllers/hooks.controller.js.map +1 -0
- package/dist/server/modules/hooks/dtos/hook-event.dto.d.ts +41 -0
- package/dist/server/modules/hooks/dtos/hook-event.dto.js +19 -0
- package/dist/server/modules/hooks/dtos/hook-event.dto.js.map +1 -0
- package/dist/server/modules/hooks/hooks.module.d.ts +2 -0
- package/dist/server/modules/hooks/hooks.module.js +27 -0
- package/dist/server/modules/hooks/hooks.module.js.map +1 -0
- package/dist/server/modules/hooks/services/hooks-config.service.d.ts +5 -0
- package/dist/server/modules/hooks/services/hooks-config.service.js +215 -0
- package/dist/server/modules/hooks/services/hooks-config.service.js.map +1 -0
- package/dist/server/modules/hooks/services/hooks.service.d.ts +11 -0
- package/dist/server/modules/hooks/services/hooks.service.js +83 -0
- package/dist/server/modules/hooks/services/hooks.service.js.map +1 -0
- package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +14 -14
- package/dist/server/modules/mcp/mcp.module.js +2 -2
- package/dist/server/modules/mcp/mcp.module.js.map +1 -1
- package/dist/server/modules/orchestrator/docker/docker.module.d.ts +2 -0
- package/dist/server/modules/orchestrator/docker/docker.module.js +22 -0
- package/dist/server/modules/orchestrator/docker/docker.module.js.map +1 -0
- package/dist/server/modules/orchestrator/docker/index.d.ts +3 -0
- package/dist/server/modules/orchestrator/docker/index.js +20 -0
- package/dist/server/modules/orchestrator/docker/index.js.map +1 -0
- package/dist/server/modules/orchestrator/docker/services/docker.service.d.ts +85 -0
- package/dist/server/modules/orchestrator/docker/services/docker.service.js +745 -0
- package/dist/server/modules/orchestrator/docker/services/docker.service.js.map +1 -0
- package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.d.ts +11 -0
- package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js +181 -0
- package/dist/server/modules/orchestrator/docker/services/seed-preparation.service.js.map +1 -0
- package/dist/server/modules/orchestrator/git/controllers/git.controller.d.ts +8 -0
- package/dist/server/modules/orchestrator/git/controllers/git.controller.js +38 -0
- package/dist/server/modules/orchestrator/git/controllers/git.controller.js.map +1 -0
- package/dist/server/modules/orchestrator/git/git.module.d.ts +2 -0
- package/dist/server/modules/orchestrator/git/git.module.js +23 -0
- package/dist/server/modules/orchestrator/git/git.module.js.map +1 -0
- package/dist/server/modules/orchestrator/git/index.d.ts +3 -0
- package/dist/server/modules/orchestrator/git/index.js +20 -0
- package/dist/server/modules/orchestrator/git/index.js.map +1 -0
- package/dist/server/modules/orchestrator/git/services/git-worktree.service.d.ts +83 -0
- package/dist/server/modules/orchestrator/git/services/git-worktree.service.js +474 -0
- package/dist/server/modules/orchestrator/git/services/git-worktree.service.js.map +1 -0
- package/dist/server/modules/orchestrator/index.d.ts +6 -0
- package/dist/server/modules/orchestrator/index.js +23 -0
- package/dist/server/modules/orchestrator/index.js.map +1 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/db/index.d.ts +1 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/db/index.js +18 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/db/index.js.map +1 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/db/orchestrator.provider.d.ts +5 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/db/orchestrator.provider.js +10 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/db/orchestrator.provider.js.map +1 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/index.d.ts +2 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/index.js +19 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/index.js.map +1 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/orchestrator-storage.module.d.ts +2 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/orchestrator-storage.module.js +23 -0
- package/dist/server/modules/orchestrator/orchestrator-storage/orchestrator-storage.module.js.map +1 -0
- package/dist/server/modules/orchestrator/proxy/index.d.ts +2 -0
- package/dist/server/modules/orchestrator/proxy/index.js +19 -0
- package/dist/server/modules/orchestrator/proxy/index.js.map +1 -0
- package/dist/server/modules/orchestrator/proxy/orchestrator-proxy.module.d.ts +2 -0
- package/dist/server/modules/orchestrator/proxy/orchestrator-proxy.module.js +22 -0
- package/dist/server/modules/orchestrator/proxy/orchestrator-proxy.module.js.map +1 -0
- package/dist/server/modules/orchestrator/proxy/services/orchestrator-proxy.service.d.ts +18 -0
- package/dist/server/modules/orchestrator/proxy/services/orchestrator-proxy.service.js +192 -0
- package/dist/server/modules/orchestrator/proxy/services/orchestrator-proxy.service.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/controllers/overview.controller.d.ts +9 -0
- package/dist/server/modules/orchestrator/sync/controllers/overview.controller.js +66 -0
- package/dist/server/modules/orchestrator/sync/controllers/overview.controller.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/dtos/overview.dto.d.ts +52 -0
- package/dist/server/modules/orchestrator/sync/dtos/overview.dto.js +3 -0
- package/dist/server/modules/orchestrator/sync/dtos/overview.dto.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/dtos/task-merge.dto.d.ts +5 -0
- package/dist/server/modules/orchestrator/sync/dtos/task-merge.dto.js +3 -0
- package/dist/server/modules/orchestrator/sync/dtos/task-merge.dto.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/events/task-merge.events.d.ts +4 -0
- package/dist/server/modules/orchestrator/sync/events/task-merge.events.js +5 -0
- package/dist/server/modules/orchestrator/sync/events/task-merge.events.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/index.d.ts +7 -0
- package/dist/server/modules/orchestrator/sync/index.js +24 -0
- package/dist/server/modules/orchestrator/sync/index.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/services/lazy-fetch.service.d.ts +31 -0
- package/dist/server/modules/orchestrator/sync/services/lazy-fetch.service.js +410 -0
- package/dist/server/modules/orchestrator/sync/services/lazy-fetch.service.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/services/task-merge.service.d.ts +44 -0
- package/dist/server/modules/orchestrator/sync/services/task-merge.service.js +730 -0
- package/dist/server/modules/orchestrator/sync/services/task-merge.service.js.map +1 -0
- package/dist/server/modules/orchestrator/sync/sync.module.d.ts +2 -0
- package/dist/server/modules/orchestrator/sync/sync.module.js +36 -0
- package/dist/server/modules/orchestrator/sync/sync.module.js.map +1 -0
- package/dist/server/modules/orchestrator/ui/app/lib/worktrees.d.ts +118 -0
- package/dist/server/modules/orchestrator/ui/app/lib/worktrees.js +297 -0
- package/dist/server/modules/orchestrator/ui/app/lib/worktrees.js.map +1 -0
- package/dist/server/modules/orchestrator/ui/app/orchestrator-app.d.ts +17 -0
- package/dist/server/modules/orchestrator/ui/app/orchestrator-app.js +752 -0
- package/dist/server/modules/orchestrator/ui/app/orchestrator-app.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/controllers/templates.controller.d.ts +15 -0
- package/dist/server/modules/orchestrator/worktrees/controllers/templates.controller.js +85 -0
- package/dist/server/modules/orchestrator/worktrees/controllers/templates.controller.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/controllers/worktrees.controller.d.ts +29 -0
- package/dist/server/modules/orchestrator/worktrees/controllers/worktrees.controller.js +272 -0
- package/dist/server/modules/orchestrator/worktrees/controllers/worktrees.controller.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/dtos/worktree.dto.d.ts +109 -0
- package/dist/server/modules/orchestrator/worktrees/dtos/worktree.dto.js +57 -0
- package/dist/server/modules/orchestrator/worktrees/dtos/worktree.dto.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/events/worktree.events.d.ts +4 -0
- package/dist/server/modules/orchestrator/worktrees/events/worktree.events.js +5 -0
- package/dist/server/modules/orchestrator/worktrees/events/worktree.events.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/index.d.ts +7 -0
- package/dist/server/modules/orchestrator/worktrees/index.js +24 -0
- package/dist/server/modules/orchestrator/worktrees/index.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/local-worktrees.store.d.ts +18 -0
- package/dist/server/modules/orchestrator/worktrees/local-worktrees.store.js +198 -0
- package/dist/server/modules/orchestrator/worktrees/local-worktrees.store.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/services/worktrees.service.d.ts +87 -0
- package/dist/server/modules/orchestrator/worktrees/services/worktrees.service.js +1380 -0
- package/dist/server/modules/orchestrator/worktrees/services/worktrees.service.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/worktree-validation.d.ts +4 -0
- package/dist/server/modules/orchestrator/worktrees/worktree-validation.js +43 -0
- package/dist/server/modules/orchestrator/worktrees/worktree-validation.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/worktrees.module.d.ts +2 -0
- package/dist/server/modules/orchestrator/worktrees/worktrees.module.js +44 -0
- package/dist/server/modules/orchestrator/worktrees/worktrees.module.js.map +1 -0
- package/dist/server/modules/orchestrator/worktrees/worktrees.store.d.ts +38 -0
- package/dist/server/modules/orchestrator/worktrees/worktrees.store.js +5 -0
- package/dist/server/modules/orchestrator/worktrees/worktrees.store.js.map +1 -0
- package/dist/server/modules/profiles/dto.d.ts +4 -4
- package/dist/server/modules/projects/controllers/projects.controller.d.ts +13 -3
- package/dist/server/modules/projects/controllers/projects.controller.js +55 -7
- package/dist/server/modules/projects/controllers/projects.controller.js.map +1 -1
- package/dist/server/modules/projects/projects.module.js +3 -2
- package/dist/server/modules/projects/projects.module.js.map +1 -1
- package/dist/server/modules/projects/services/main-project-bootstrap.service.d.ts +11 -0
- package/dist/server/modules/projects/services/main-project-bootstrap.service.js +76 -0
- package/dist/server/modules/projects/services/main-project-bootstrap.service.js.map +1 -0
- package/dist/server/modules/projects/services/projects.service.d.ts +8 -0
- package/dist/server/modules/projects/services/projects.service.js +115 -37
- package/dist/server/modules/projects/services/projects.service.js.map +1 -1
- package/dist/server/modules/providers/adapters/gemini.adapter.d.ts +1 -1
- package/dist/server/modules/providers/adapters/gemini.adapter.js +6 -4
- package/dist/server/modules/providers/adapters/gemini.adapter.js.map +1 -1
- package/dist/server/modules/providers/controllers/providers.controller.d.ts +3 -0
- package/dist/server/modules/providers/controllers/providers.controller.js +28 -0
- package/dist/server/modules/providers/controllers/providers.controller.js.map +1 -1
- package/dist/server/modules/providers/providers.module.js +2 -2
- package/dist/server/modules/providers/providers.module.js.map +1 -1
- package/dist/server/modules/registry/services/unified-template.service.js +6 -18
- package/dist/server/modules/registry/services/unified-template.service.js.map +1 -1
- package/dist/server/modules/reviews/dtos/review.dto.d.ts +4 -4
- package/dist/server/modules/reviews/reviews.module.js +2 -2
- package/dist/server/modules/reviews/reviews.module.js.map +1 -1
- package/dist/server/modules/seeders/seeders/0005_seed_renew_instructions_subscriber.d.ts +3 -0
- package/dist/server/modules/seeders/seeders/0005_seed_renew_instructions_subscriber.js +89 -0
- package/dist/server/modules/seeders/seeders/0005_seed_renew_instructions_subscriber.js.map +1 -0
- package/dist/server/modules/seeders/seeders/0006_seed_rename_template_slugs.d.ts +3 -0
- package/dist/server/modules/seeders/seeders/0006_seed_rename_template_slugs.js +60 -0
- package/dist/server/modules/seeders/seeders/0006_seed_rename_template_slugs.js.map +1 -0
- package/dist/server/modules/seeders/services/data-seeder.service.js +4 -0
- package/dist/server/modules/seeders/services/data-seeder.service.js.map +1 -1
- package/dist/server/modules/sessions/services/sessions.service.d.ts +3 -1
- package/dist/server/modules/sessions/services/sessions.service.js +79 -22
- package/dist/server/modules/sessions/services/sessions.service.js.map +1 -1
- package/dist/server/modules/sessions/sessions.module.js +6 -4
- package/dist/server/modules/sessions/sessions.module.js.map +1 -1
- package/dist/server/modules/sessions/utils/claude-config.d.ts +6 -2
- package/dist/server/modules/sessions/utils/claude-config.js +19 -6
- package/dist/server/modules/sessions/utils/claude-config.js.map +1 -1
- package/dist/server/modules/settings/dtos/settings.dto.d.ts +4 -4
- package/dist/server/modules/skills/dtos/community-sources.dto.d.ts +2 -2
- package/dist/server/modules/skills/dtos/local-sources.dto.d.ts +2 -2
- package/dist/server/modules/skills/dtos/skill.dto.d.ts +7 -7
- package/dist/server/modules/storage/db/schema.d.ts +811 -0
- package/dist/server/modules/storage/db/schema.js +63 -1
- package/dist/server/modules/storage/db/schema.js.map +1 -1
- package/dist/server/modules/storage/db/sqlite-json.d.ts +2 -0
- package/dist/server/modules/storage/db/sqlite-json.js +8 -0
- package/dist/server/modules/storage/db/sqlite-json.js.map +1 -0
- package/dist/server/modules/storage/interfaces/storage.interface.d.ts +4 -1
- package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
- package/dist/server/modules/storage/local/local-storage.service.d.ts +1 -1
- package/dist/server/modules/storage/local/local-storage.service.js +19 -2
- package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
- package/dist/server/modules/storage/models/domain.models.d.ts +2 -0
- package/dist/server/modules/subscribers/dtos/subscriber.dto.d.ts +16 -16
- package/dist/server/modules/subscribers/events/event-fields-catalog.js +18 -0
- package/dist/server/modules/subscribers/events/event-fields-catalog.js.map +1 -1
- package/dist/server/modules/subscribers/subscribers.module.js +2 -2
- package/dist/server/modules/subscribers/subscribers.module.js.map +1 -1
- package/dist/server/modules/terminal/gateways/terminal.gateway.js +7 -2
- package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -1
- package/dist/server/modules/terminal/services/tmux.service.d.ts +9 -0
- package/dist/server/modules/terminal/services/tmux.service.js +55 -5
- package/dist/server/modules/terminal/services/tmux.service.js.map +1 -1
- package/dist/server/modules/terminal/terminal.module.js +2 -2
- package/dist/server/modules/terminal/terminal.module.js.map +1 -1
- package/dist/server/modules/watchers/watchers.module.js +2 -2
- package/dist/server/modules/watchers/watchers.module.js.map +1 -1
- package/dist/server/templates/3-agents-dev.json +662 -0
- package/dist/server/templates/{dev-loop.json → 5-agents-dev.json} +174 -100
- package/dist/server/test-setup.js +7 -0
- package/dist/server/test-setup.js.map +1 -1
- package/dist/server/tsconfig.tsbuildinfo +1 -1
- package/dist/server/ui/assets/ReviewDetailPage-CZZQtaY7.js +1 -0
- package/dist/server/ui/assets/{ReviewsPage-MKT-vv59.js → ReviewsPage-C209GLQG.js} +1 -1
- package/dist/server/ui/assets/index-DvRuLfpZ.css +32 -0
- package/dist/server/ui/assets/index-Th1FDtKR.js +977 -0
- package/dist/server/ui/assets/{useReviewSubscription-Dc58i6Bk.js → useReviewSubscription-Dcabsa78.js} +1 -1
- package/dist/server/ui/index.html +2 -2
- package/dist/templates/3-agents-dev.json +662 -0
- package/dist/templates/{dev-loop.json → 5-agents-dev.json} +174 -100
- package/package.json +15 -1
- package/dist/server/ui/assets/ReviewDetailPage-BvSckWKj.js +0 -6
- package/dist/server/ui/assets/index-BtUq-Qxb.css +0 -32
- package/dist/server/ui/assets/index-kTb634Zp.js +0 -945
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.OrchestratorDockerService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const child_process_1 = require("child_process");
|
|
18
|
+
const Dockerode = require("dockerode");
|
|
19
|
+
const fs_1 = require("fs");
|
|
20
|
+
const os_1 = require("os");
|
|
21
|
+
const path_1 = require("path");
|
|
22
|
+
const logger_1 = require("../../../../common/logging/logger");
|
|
23
|
+
const templates_directory_1 = require("../../../../common/templates-directory");
|
|
24
|
+
const logger = (0, logger_1.createLogger)('OrchestratorDockerService');
|
|
25
|
+
const DOCKER_SOCKET_PATH = '/var/run/docker.sock';
|
|
26
|
+
const DEFAULT_CONTAINER_PORT = 3000;
|
|
27
|
+
const DEFAULT_CONTAINER_HOME_PATH = '/home/node';
|
|
28
|
+
const DEFAULT_CONTAINER_PROJECT_PATH = '/project';
|
|
29
|
+
const DEFAULT_CONTAINER_DATA_PATH = `${DEFAULT_CONTAINER_HOME_PATH}/.devchain`;
|
|
30
|
+
const DEFAULT_CONTAINER_SKILLS_SEED_PATH = '/seed-skills';
|
|
31
|
+
const DEFAULT_CONTAINER_TEMPLATES_PATH = '/app/apps/local-app/dist/templates';
|
|
32
|
+
const DEFAULT_CONTAINER_REGISTRY_CACHE_PATH = `${DEFAULT_CONTAINER_HOME_PATH}/.devchain/registry-cache`;
|
|
33
|
+
const WORKTREE_NETWORK_PREFIX = 'devchain-wt-';
|
|
34
|
+
const WORKTREE_NETWORK_SUFFIX = '-net';
|
|
35
|
+
const WORKTREE_CONTAINER_PREFIX = 'devchain-wt-';
|
|
36
|
+
const COMPOSE_PROJECT_LABEL = 'com.docker.compose.project';
|
|
37
|
+
const COMPOSE_DEFAULT_NETWORK_SUFFIX = '_default';
|
|
38
|
+
const HEALTH_POLL_INTERVAL_MS = 1000;
|
|
39
|
+
const HEALTH_REQUEST_TIMEOUT_MS = 1500;
|
|
40
|
+
function readGitConfig(worktreePath, key) {
|
|
41
|
+
try {
|
|
42
|
+
return ((0, child_process_1.execFileSync)('git', ['-C', worktreePath, 'config', key], { encoding: 'utf-8' }).trim() || null);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
let OrchestratorDockerService = class OrchestratorDockerService {
|
|
49
|
+
constructor(docker = new Dockerode({ socketPath: DOCKER_SOCKET_PATH })) {
|
|
50
|
+
this.docker = docker;
|
|
51
|
+
}
|
|
52
|
+
async createContainer(config) {
|
|
53
|
+
const containerPort = config.containerPort ?? DEFAULT_CONTAINER_PORT;
|
|
54
|
+
const image = this.resolveContainerImage(config.image);
|
|
55
|
+
const worktreeName = this.resolveWorktreeName(config);
|
|
56
|
+
await this.ensureImageAvailable(image);
|
|
57
|
+
const networkName = await this.ensureWorktreeNetwork(worktreeName);
|
|
58
|
+
const envMap = this.buildEnvMap(config.env, worktreeName);
|
|
59
|
+
const gitUserName = readGitConfig(config.worktreePath, 'user.name');
|
|
60
|
+
const gitUserEmail = readGitConfig(config.worktreePath, 'user.email');
|
|
61
|
+
if (gitUserName) {
|
|
62
|
+
envMap.GIT_AUTHOR_NAME ??= gitUserName;
|
|
63
|
+
envMap.GIT_COMMITTER_NAME ??= gitUserName;
|
|
64
|
+
}
|
|
65
|
+
if (gitUserEmail) {
|
|
66
|
+
envMap.GIT_AUTHOR_EMAIL ??= gitUserEmail;
|
|
67
|
+
envMap.GIT_COMMITTER_EMAIL ??= gitUserEmail;
|
|
68
|
+
}
|
|
69
|
+
const authMounts = this.discoverProviderAuthMounts();
|
|
70
|
+
if (!envMap.ENABLED_PROVIDERS) {
|
|
71
|
+
envMap.ENABLED_PROVIDERS = authMounts.map((mount) => mount.provider).join(',');
|
|
72
|
+
}
|
|
73
|
+
const binds = this.buildBindMounts(config, authMounts);
|
|
74
|
+
const labels = this.buildLabels(config, worktreeName);
|
|
75
|
+
logger.info({
|
|
76
|
+
name: config.name,
|
|
77
|
+
worktreeName,
|
|
78
|
+
networkName,
|
|
79
|
+
image,
|
|
80
|
+
bindCount: binds.length,
|
|
81
|
+
providers: envMap.ENABLED_PROVIDERS,
|
|
82
|
+
}, 'Creating orchestrator worktree container');
|
|
83
|
+
const container = await this.docker.createContainer({
|
|
84
|
+
name: config.name,
|
|
85
|
+
Image: image,
|
|
86
|
+
Env: Object.entries(envMap).map(([key, value]) => `${key}=${value}`),
|
|
87
|
+
Cmd: config.command,
|
|
88
|
+
Entrypoint: config.entrypoint,
|
|
89
|
+
Labels: labels,
|
|
90
|
+
ExposedPorts: {
|
|
91
|
+
[`${containerPort}/tcp`]: {},
|
|
92
|
+
},
|
|
93
|
+
HostConfig: {
|
|
94
|
+
Binds: binds,
|
|
95
|
+
CapAdd: ['SYS_PTRACE'],
|
|
96
|
+
NetworkMode: networkName,
|
|
97
|
+
PortBindings: {
|
|
98
|
+
[`${containerPort}/tcp`]: [{ HostIp: '127.0.0.1', HostPort: '' }],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
await container.start();
|
|
103
|
+
await this.ensureWorktreeOnComposeNetwork(worktreeName, container.id).catch((error) => {
|
|
104
|
+
logger.warn({ error, worktreeName, containerId: container.id }, 'Failed attaching worktree container to compose default network');
|
|
105
|
+
});
|
|
106
|
+
const inspect = await container.inspect();
|
|
107
|
+
return {
|
|
108
|
+
id: container.id,
|
|
109
|
+
name: inspect.Name.replace(/^\//, ''),
|
|
110
|
+
image,
|
|
111
|
+
hostPort: this.extractHostPort(inspect, containerPort),
|
|
112
|
+
state: inspect.State?.Status ?? 'unknown',
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
async startContainer(containerId) {
|
|
116
|
+
await this.docker.getContainer(containerId).start();
|
|
117
|
+
}
|
|
118
|
+
async getContainerHostPort(containerId, containerPort = DEFAULT_CONTAINER_PORT) {
|
|
119
|
+
const inspect = await this.docker.getContainer(containerId).inspect();
|
|
120
|
+
return this.extractHostPort(inspect, containerPort);
|
|
121
|
+
}
|
|
122
|
+
async stopContainer(containerId) {
|
|
123
|
+
await this.docker.getContainer(containerId).stop({ t: 30 });
|
|
124
|
+
}
|
|
125
|
+
async removeContainer(containerId, force = false) {
|
|
126
|
+
await this.docker.getContainer(containerId).remove({ force });
|
|
127
|
+
}
|
|
128
|
+
async inspectContainer(containerId) {
|
|
129
|
+
return this.docker.getContainer(containerId).inspect();
|
|
130
|
+
}
|
|
131
|
+
async getContainerLogs(containerId, tail = 200) {
|
|
132
|
+
const logs = await this.docker.getContainer(containerId).logs({
|
|
133
|
+
stdout: true,
|
|
134
|
+
stderr: true,
|
|
135
|
+
timestamps: false,
|
|
136
|
+
tail,
|
|
137
|
+
});
|
|
138
|
+
if (Buffer.isBuffer(logs)) {
|
|
139
|
+
return this.decodeDockerMultiplexStream(logs);
|
|
140
|
+
}
|
|
141
|
+
const output = await this.readStreamFully(logs);
|
|
142
|
+
return this.decodeDockerMultiplexStream(output);
|
|
143
|
+
}
|
|
144
|
+
async waitForHealthy(containerId, timeoutMs) {
|
|
145
|
+
if (timeoutMs <= 0) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
const deadline = Date.now() + timeoutMs;
|
|
149
|
+
while (Date.now() < deadline) {
|
|
150
|
+
const inspect = await this.inspectContainer(containerId);
|
|
151
|
+
const hostPort = this.extractHostPort(inspect, DEFAULT_CONTAINER_PORT);
|
|
152
|
+
if (hostPort) {
|
|
153
|
+
const isHealthy = await this.checkReadyEndpoint(hostPort);
|
|
154
|
+
if (isHealthy) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
await this.sleep(HEALTH_POLL_INTERVAL_MS);
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
async execInContainer(containerId, cmd, options) {
|
|
163
|
+
const exec = await this.docker.getContainer(containerId).exec({
|
|
164
|
+
Cmd: cmd,
|
|
165
|
+
AttachStdout: true,
|
|
166
|
+
AttachStderr: true,
|
|
167
|
+
Tty: false,
|
|
168
|
+
WorkingDir: options?.workingDir,
|
|
169
|
+
});
|
|
170
|
+
const stream = await exec.start({ hijack: false, stdin: false });
|
|
171
|
+
const outputBuffer = await this.readStreamFully(stream);
|
|
172
|
+
const inspect = await exec.inspect();
|
|
173
|
+
return {
|
|
174
|
+
exitCode: inspect.ExitCode ?? 1,
|
|
175
|
+
output: this.decodeDockerMultiplexStream(outputBuffer),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async subscribeToContainerEvents(onEvent) {
|
|
179
|
+
const filters = JSON.stringify({ type: ['container'] });
|
|
180
|
+
const stream = await this.docker.getEvents({ filters });
|
|
181
|
+
let buffered = '';
|
|
182
|
+
const onData = (chunk) => {
|
|
183
|
+
buffered += Buffer.isBuffer(chunk) ? chunk.toString('utf8') : chunk;
|
|
184
|
+
const lines = buffered.split('\n');
|
|
185
|
+
buffered = lines.pop() ?? '';
|
|
186
|
+
for (const line of lines) {
|
|
187
|
+
if (!line.trim()) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const parsed = JSON.parse(line);
|
|
192
|
+
onEvent(parsed);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
logger.warn({ error, line }, 'Failed parsing docker event line');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const onError = (error) => {
|
|
200
|
+
logger.warn({ error }, 'Docker events stream error');
|
|
201
|
+
};
|
|
202
|
+
stream.on('data', onData);
|
|
203
|
+
stream.on('error', onError);
|
|
204
|
+
return () => {
|
|
205
|
+
stream.off('data', onData);
|
|
206
|
+
stream.off('error', onError);
|
|
207
|
+
if ('destroy' in stream && typeof stream.destroy === 'function') {
|
|
208
|
+
stream.destroy();
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
async ping() {
|
|
213
|
+
try {
|
|
214
|
+
await this.docker.ping();
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async cleanupWorktreeProjectContainers(worktreeName, worktreeContainerId) {
|
|
222
|
+
const normalizedWorktreeName = worktreeName.trim();
|
|
223
|
+
if (!normalizedWorktreeName) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const networkName = this.getWorktreeNetworkName(normalizedWorktreeName);
|
|
227
|
+
const worktreeContainerName = this.getWorktreeContainerName(normalizedWorktreeName);
|
|
228
|
+
if (worktreeContainerId) {
|
|
229
|
+
await this.tryComposeDown(normalizedWorktreeName, worktreeContainerId);
|
|
230
|
+
}
|
|
231
|
+
await this.cleanupComposeProjectContainers({
|
|
232
|
+
composeProjectName: normalizedWorktreeName,
|
|
233
|
+
worktreeContainerId: worktreeContainerId ?? undefined,
|
|
234
|
+
worktreeContainerName,
|
|
235
|
+
});
|
|
236
|
+
await this.cleanupComposeProjectNetworks(normalizedWorktreeName);
|
|
237
|
+
await this.cleanupContainersAttachedToNetwork({
|
|
238
|
+
networkName,
|
|
239
|
+
worktreeContainerId: worktreeContainerId ?? undefined,
|
|
240
|
+
worktreeContainerName,
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
async ensureWorktreeOnComposeNetwork(worktreeName, containerId) {
|
|
244
|
+
const normalizedWorktreeName = worktreeName.trim();
|
|
245
|
+
const normalizedContainerId = containerId.trim();
|
|
246
|
+
if (!normalizedWorktreeName || !normalizedContainerId) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const composeNetworkName = this.getComposeDefaultNetworkName(normalizedWorktreeName);
|
|
250
|
+
const network = this.docker.getNetwork(composeNetworkName);
|
|
251
|
+
let inspect;
|
|
252
|
+
try {
|
|
253
|
+
inspect = await network.inspect();
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
if (this.isDockerNotFoundError(error)) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
logger.warn({ error, worktreeName: normalizedWorktreeName, networkName: composeNetworkName }, 'Failed inspecting compose network for worktree attachment');
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const attached = Object.keys(inspect.Containers ?? {}).some((id) => this.matchesContainerId(normalizedContainerId, id));
|
|
263
|
+
if (attached) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
if (typeof network.connect !== 'function') {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
try {
|
|
270
|
+
await network.connect({ Container: normalizedContainerId });
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
if (this.isDockerNotFoundError(error) || this.isAlreadyConnectedError(error)) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
logger.warn({
|
|
277
|
+
error,
|
|
278
|
+
worktreeName: normalizedWorktreeName,
|
|
279
|
+
containerId: normalizedContainerId,
|
|
280
|
+
networkName: composeNetworkName,
|
|
281
|
+
}, 'Failed connecting worktree container to compose network');
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
async removeWorktreeNetwork(worktreeName) {
|
|
285
|
+
const normalizedWorktreeName = worktreeName.trim();
|
|
286
|
+
if (!normalizedWorktreeName) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
await this.removeNetworkByName(this.getWorktreeNetworkName(normalizedWorktreeName));
|
|
290
|
+
await this.removeNetworkByName(this.getComposeDefaultNetworkName(normalizedWorktreeName));
|
|
291
|
+
}
|
|
292
|
+
buildEnvMap(userEnv, worktreeName) {
|
|
293
|
+
const envMap = {
|
|
294
|
+
HOST: '0.0.0.0',
|
|
295
|
+
NODE_ENV: 'production',
|
|
296
|
+
...(userEnv ?? {}),
|
|
297
|
+
};
|
|
298
|
+
if (!envMap.COMPOSE_PROJECT_NAME) {
|
|
299
|
+
envMap.COMPOSE_PROJECT_NAME = worktreeName;
|
|
300
|
+
}
|
|
301
|
+
return envMap;
|
|
302
|
+
}
|
|
303
|
+
buildLabels(config, worktreeName) {
|
|
304
|
+
return {
|
|
305
|
+
'devchain.worktree': worktreeName,
|
|
306
|
+
...(config.labels ?? {}),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
buildBindMounts(config, authMounts) {
|
|
310
|
+
const containerProjectPath = config.containerProjectPath ?? DEFAULT_CONTAINER_PROJECT_PATH;
|
|
311
|
+
const containerDataPath = config.containerDataPath ?? DEFAULT_CONTAINER_DATA_PATH;
|
|
312
|
+
const binds = [
|
|
313
|
+
`${config.worktreePath}:${containerProjectPath}:rw`,
|
|
314
|
+
`${config.dataPath}:${containerDataPath}:rw`,
|
|
315
|
+
...authMounts.map((mount) => mount.bind),
|
|
316
|
+
...(config.additionalBinds ?? []),
|
|
317
|
+
`${DOCKER_SOCKET_PATH}:${DOCKER_SOCKET_PATH}:rw`,
|
|
318
|
+
];
|
|
319
|
+
const gitCommonDirMount = this.discoverGitCommonDirMount(config.worktreePath);
|
|
320
|
+
if (gitCommonDirMount) {
|
|
321
|
+
binds.push(gitCommonDirMount);
|
|
322
|
+
}
|
|
323
|
+
const skillsSeedMount = this.discoverSkillsSeedMount();
|
|
324
|
+
if (skillsSeedMount) {
|
|
325
|
+
binds.push(skillsSeedMount);
|
|
326
|
+
}
|
|
327
|
+
const templatesMounts = this.discoverTemplatesMounts();
|
|
328
|
+
binds.push(...templatesMounts);
|
|
329
|
+
return binds;
|
|
330
|
+
}
|
|
331
|
+
discoverGitCommonDirMount(worktreePath) {
|
|
332
|
+
const gitPath = (0, path_1.join)(worktreePath, '.git');
|
|
333
|
+
if (!(0, fs_1.existsSync)(gitPath)) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
let gitPathStats;
|
|
337
|
+
try {
|
|
338
|
+
gitPathStats = (0, fs_1.statSync)(gitPath);
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
logger.warn({ error, gitPath }, 'Failed to stat .git path while resolving bind mounts');
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
if (gitPathStats.isDirectory()) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
if (!gitPathStats.isFile()) {
|
|
348
|
+
logger.warn({ gitPath }, 'Skipping git metadata mount: .git is not a file or directory');
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
let gitdirPath = null;
|
|
352
|
+
try {
|
|
353
|
+
const gitFileContents = (0, fs_1.readFileSync)(gitPath, 'utf8');
|
|
354
|
+
const gitdirLine = gitFileContents
|
|
355
|
+
.split(/\r?\n/)
|
|
356
|
+
.find((line) => line.trimStart().toLowerCase().startsWith('gitdir:'));
|
|
357
|
+
if (gitdirLine) {
|
|
358
|
+
gitdirPath = gitdirLine.slice(gitdirLine.indexOf(':') + 1).trim();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
logger.warn({ error, gitPath }, 'Failed to read .git file while resolving bind mounts');
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
if (!gitdirPath) {
|
|
366
|
+
logger.warn({ gitPath }, 'Skipping git metadata mount: .git file missing gitdir entry');
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
if (!path_1.posix.isAbsolute(gitdirPath)) {
|
|
370
|
+
logger.warn({ gitPath, gitdirPath }, 'Skipping git metadata mount: gitdir path is not POSIX-absolute');
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
const repoGitCommonDir = (0, path_1.dirname)((0, path_1.dirname)(gitdirPath));
|
|
374
|
+
try {
|
|
375
|
+
const commonDirStats = (0, fs_1.statSync)(repoGitCommonDir);
|
|
376
|
+
if (!commonDirStats.isDirectory()) {
|
|
377
|
+
logger.warn({ gitPath, gitdirPath, repoGitCommonDir }, 'Skipping git metadata mount: computed git common dir is not a directory');
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
logger.warn({ error, gitPath, gitdirPath, repoGitCommonDir }, 'Skipping git metadata mount: computed git common dir does not exist');
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
return `${repoGitCommonDir}:${repoGitCommonDir}:rw`;
|
|
386
|
+
}
|
|
387
|
+
discoverProviderAuthMounts() {
|
|
388
|
+
const home = this.getHostHomeDir();
|
|
389
|
+
const providers = [
|
|
390
|
+
{
|
|
391
|
+
provider: 'claude',
|
|
392
|
+
source: (0, path_1.join)(home, '.claude', '.credentials.json'),
|
|
393
|
+
target: `${DEFAULT_CONTAINER_HOME_PATH}/.claude/.credentials.json`,
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
provider: 'codex',
|
|
397
|
+
source: (0, path_1.join)(home, '.codex', 'auth.json'),
|
|
398
|
+
target: `${DEFAULT_CONTAINER_HOME_PATH}/.codex/auth.json`,
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
provider: 'gemini',
|
|
402
|
+
source: (0, path_1.join)(home, '.gemini', 'oauth_creds.json'),
|
|
403
|
+
target: `${DEFAULT_CONTAINER_HOME_PATH}/.gemini/oauth_creds.json`,
|
|
404
|
+
},
|
|
405
|
+
];
|
|
406
|
+
return providers
|
|
407
|
+
.filter((provider) => (0, fs_1.existsSync)(provider.source))
|
|
408
|
+
.map((provider) => ({
|
|
409
|
+
provider: provider.provider,
|
|
410
|
+
bind: `${provider.source}:${provider.target}:ro`,
|
|
411
|
+
}));
|
|
412
|
+
}
|
|
413
|
+
discoverSkillsSeedMount() {
|
|
414
|
+
const hostSkillsPath = (0, path_1.join)(this.getHostHomeDir(), '.devchain', 'skills');
|
|
415
|
+
if (!(0, fs_1.existsSync)(hostSkillsPath)) {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
return `${hostSkillsPath}:${DEFAULT_CONTAINER_SKILLS_SEED_PATH}:ro`;
|
|
419
|
+
}
|
|
420
|
+
discoverTemplatesMounts() {
|
|
421
|
+
const mounts = [];
|
|
422
|
+
const hostTemplatesDir = (0, templates_directory_1.resolveTemplatesDirectory)(__dirname);
|
|
423
|
+
if (hostTemplatesDir) {
|
|
424
|
+
mounts.push(`${hostTemplatesDir}:${DEFAULT_CONTAINER_TEMPLATES_PATH}:ro`);
|
|
425
|
+
}
|
|
426
|
+
const hostRegistryCache = (0, path_1.join)(this.getHostHomeDir(), '.devchain', 'registry-cache');
|
|
427
|
+
if ((0, fs_1.existsSync)(hostRegistryCache)) {
|
|
428
|
+
mounts.push(`${hostRegistryCache}:${DEFAULT_CONTAINER_REGISTRY_CACHE_PATH}:ro`);
|
|
429
|
+
}
|
|
430
|
+
return mounts;
|
|
431
|
+
}
|
|
432
|
+
getHostHomeDir() {
|
|
433
|
+
return process.env.HOME?.trim() || (0, os_1.homedir)();
|
|
434
|
+
}
|
|
435
|
+
resolveWorktreeName(config) {
|
|
436
|
+
const explicit = config.worktreeName?.trim();
|
|
437
|
+
if (explicit) {
|
|
438
|
+
return explicit;
|
|
439
|
+
}
|
|
440
|
+
const derived = config.name.replace(/^devchain-wt-/, '').trim();
|
|
441
|
+
return derived || config.name.trim();
|
|
442
|
+
}
|
|
443
|
+
resolveContainerImage(explicitImage) {
|
|
444
|
+
const imageFromConfig = explicitImage?.trim();
|
|
445
|
+
if (imageFromConfig) {
|
|
446
|
+
return imageFromConfig;
|
|
447
|
+
}
|
|
448
|
+
const imageFromEnv = process.env.ORCHESTRATOR_CONTAINER_IMAGE?.trim();
|
|
449
|
+
if (imageFromEnv) {
|
|
450
|
+
return imageFromEnv;
|
|
451
|
+
}
|
|
452
|
+
const mode = process.env.DEVCHAIN_MODE?.trim();
|
|
453
|
+
if (mode === 'orchestrator' || mode === 'main') {
|
|
454
|
+
throw new Error('ORCHESTRATOR_CONTAINER_IMAGE is required in container mode. Set it to a versioned GHCR image reference.');
|
|
455
|
+
}
|
|
456
|
+
throw new Error('Container image is required. Provide config.image or set ORCHESTRATOR_CONTAINER_IMAGE.');
|
|
457
|
+
}
|
|
458
|
+
getWorktreeNetworkName(worktreeName) {
|
|
459
|
+
return `${WORKTREE_NETWORK_PREFIX}${worktreeName}${WORKTREE_NETWORK_SUFFIX}`;
|
|
460
|
+
}
|
|
461
|
+
getWorktreeContainerName(worktreeName) {
|
|
462
|
+
return `${WORKTREE_CONTAINER_PREFIX}${worktreeName}`;
|
|
463
|
+
}
|
|
464
|
+
getComposeDefaultNetworkName(worktreeName) {
|
|
465
|
+
return `${worktreeName}${COMPOSE_DEFAULT_NETWORK_SUFFIX}`;
|
|
466
|
+
}
|
|
467
|
+
async ensureWorktreeNetwork(worktreeName) {
|
|
468
|
+
const networkName = this.getWorktreeNetworkName(worktreeName);
|
|
469
|
+
try {
|
|
470
|
+
await this.docker.getNetwork(networkName).inspect();
|
|
471
|
+
return networkName;
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
}
|
|
475
|
+
try {
|
|
476
|
+
await this.docker.createNetwork({
|
|
477
|
+
Name: networkName,
|
|
478
|
+
CheckDuplicate: true,
|
|
479
|
+
Labels: {
|
|
480
|
+
'devchain.worktree': worktreeName,
|
|
481
|
+
'devchain.managed': 'true',
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
return networkName;
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
await this.docker.getNetwork(networkName).inspect();
|
|
488
|
+
return networkName;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async tryComposeDown(worktreeName, containerId) {
|
|
492
|
+
try {
|
|
493
|
+
const result = await this.execInContainer(containerId, ['docker', 'compose', '-p', worktreeName, 'down', '--remove-orphans'], { workingDir: DEFAULT_CONTAINER_PROJECT_PATH });
|
|
494
|
+
if (result.exitCode !== 0) {
|
|
495
|
+
logger.warn({
|
|
496
|
+
worktreeName,
|
|
497
|
+
containerId,
|
|
498
|
+
exitCode: result.exitCode,
|
|
499
|
+
output: result.output,
|
|
500
|
+
}, 'docker compose down returned non-zero exit code; continuing with fallback cleanup');
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
logger.warn({ error, worktreeName, containerId }, 'docker compose down failed; continuing with fallback cleanup');
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
async cleanupContainersAttachedToNetwork(input) {
|
|
508
|
+
const network = this.docker.getNetwork(input.networkName);
|
|
509
|
+
let inspect;
|
|
510
|
+
try {
|
|
511
|
+
inspect = await network.inspect();
|
|
512
|
+
}
|
|
513
|
+
catch (error) {
|
|
514
|
+
if (this.isDockerNotFoundError(error)) {
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
logger.warn({ error, networkName: input.networkName }, 'Failed inspecting worktree network for fallback cleanup');
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const containers = inspect.Containers ?? {};
|
|
521
|
+
for (const [containerId, containerInfo] of Object.entries(containers)) {
|
|
522
|
+
const containerName = containerInfo?.Name;
|
|
523
|
+
if ((input.worktreeContainerId &&
|
|
524
|
+
this.matchesContainerId(input.worktreeContainerId, containerId)) ||
|
|
525
|
+
containerName === input.worktreeContainerName) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
await this.stopContainer(containerId);
|
|
530
|
+
}
|
|
531
|
+
catch {
|
|
532
|
+
}
|
|
533
|
+
try {
|
|
534
|
+
await this.removeContainer(containerId, true);
|
|
535
|
+
}
|
|
536
|
+
catch (error) {
|
|
537
|
+
if (this.isDockerNotFoundError(error)) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
logger.warn({ error, containerId, networkName: input.networkName }, 'Failed removing project sub-container during fallback cleanup');
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async cleanupComposeProjectContainers(input) {
|
|
545
|
+
let containers = [];
|
|
546
|
+
try {
|
|
547
|
+
containers = await this.docker.listContainers({
|
|
548
|
+
all: true,
|
|
549
|
+
filters: {
|
|
550
|
+
label: [`${COMPOSE_PROJECT_LABEL}=${input.composeProjectName}`],
|
|
551
|
+
},
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
logger.warn({ error, composeProjectName: input.composeProjectName }, 'Failed listing compose project containers for cleanup');
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
for (const container of containers) {
|
|
559
|
+
const containerId = container.Id;
|
|
560
|
+
if (!containerId) {
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
const names = container.Names ?? [];
|
|
564
|
+
const normalizedNames = names.map((name) => name.replace(/^\//, ''));
|
|
565
|
+
if ((input.worktreeContainerId &&
|
|
566
|
+
this.matchesContainerId(input.worktreeContainerId, containerId)) ||
|
|
567
|
+
normalizedNames.includes(input.worktreeContainerName)) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
await this.stopContainer(containerId);
|
|
572
|
+
}
|
|
573
|
+
catch {
|
|
574
|
+
}
|
|
575
|
+
try {
|
|
576
|
+
await this.removeContainer(containerId, true);
|
|
577
|
+
}
|
|
578
|
+
catch (error) {
|
|
579
|
+
if (this.isDockerNotFoundError(error)) {
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
logger.warn({ error, containerId, composeProjectName: input.composeProjectName }, 'Failed removing compose project container during cleanup');
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
async cleanupComposeProjectNetworks(composeProjectName) {
|
|
587
|
+
let networks = [];
|
|
588
|
+
try {
|
|
589
|
+
networks = await this.docker.listNetworks({
|
|
590
|
+
filters: {
|
|
591
|
+
label: [`${COMPOSE_PROJECT_LABEL}=${composeProjectName}`],
|
|
592
|
+
},
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
catch (error) {
|
|
596
|
+
logger.warn({ error, composeProjectName }, 'Failed listing compose project networks for cleanup');
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
for (const networkInfo of networks) {
|
|
600
|
+
const networkId = networkInfo.Id;
|
|
601
|
+
const networkName = networkInfo.Name;
|
|
602
|
+
if (!networkId) {
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
try {
|
|
606
|
+
await this.docker.getNetwork(networkId).remove();
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
if (this.isDockerNotFoundError(error) || this.isNetworkInUseError(error)) {
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
logger.warn({ error, composeProjectName, networkId, networkName }, 'Failed removing compose project network during cleanup');
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
matchesContainerId(expected, actual) {
|
|
617
|
+
return expected === actual || expected.startsWith(actual) || actual.startsWith(expected);
|
|
618
|
+
}
|
|
619
|
+
isDockerNotFoundError(error) {
|
|
620
|
+
if (!error || typeof error !== 'object') {
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
const maybeError = error;
|
|
624
|
+
if (maybeError.statusCode === 404) {
|
|
625
|
+
return true;
|
|
626
|
+
}
|
|
627
|
+
const message = `${maybeError.reason ?? ''} ${maybeError.message ?? ''}`.toLowerCase();
|
|
628
|
+
return message.includes('not found') || message.includes('no such');
|
|
629
|
+
}
|
|
630
|
+
isAlreadyConnectedError(error) {
|
|
631
|
+
if (!error || typeof error !== 'object') {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
const maybeError = error;
|
|
635
|
+
if (maybeError.statusCode === 409) {
|
|
636
|
+
return true;
|
|
637
|
+
}
|
|
638
|
+
const message = `${maybeError.reason ?? ''} ${maybeError.message ?? ''}`.toLowerCase();
|
|
639
|
+
return (message.includes('already exists') ||
|
|
640
|
+
message.includes('already connected') ||
|
|
641
|
+
message.includes('endpoint with name'));
|
|
642
|
+
}
|
|
643
|
+
isNetworkInUseError(error) {
|
|
644
|
+
if (!error || typeof error !== 'object') {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
const maybeError = error;
|
|
648
|
+
if (maybeError.statusCode === 409) {
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
const message = `${maybeError.reason ?? ''} ${maybeError.message ?? ''}`.toLowerCase();
|
|
652
|
+
return message.includes('active endpoints') || message.includes('resource is still in use');
|
|
653
|
+
}
|
|
654
|
+
async removeNetworkByName(networkName) {
|
|
655
|
+
try {
|
|
656
|
+
await this.docker.getNetwork(networkName).remove();
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
if (this.isDockerNotFoundError(error)) {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
logger.warn({ error, networkName }, 'Failed to remove docker network');
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
async ensureImageAvailable(image) {
|
|
666
|
+
try {
|
|
667
|
+
await this.docker.getImage(image).inspect();
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
logger.info({ image }, 'Docker image not found locally; pulling');
|
|
672
|
+
}
|
|
673
|
+
const pullStream = await this.docker.pull(image);
|
|
674
|
+
await new Promise((resolve, reject) => {
|
|
675
|
+
this.docker.modem.followProgress(pullStream, (error) => error ? reject(error) : resolve());
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
extractHostPort(inspect, containerPort) {
|
|
679
|
+
const key = `${containerPort}/tcp`;
|
|
680
|
+
const bindings = inspect.NetworkSettings?.Ports?.[key];
|
|
681
|
+
const hostPortRaw = bindings?.[0]?.HostPort;
|
|
682
|
+
if (!hostPortRaw) {
|
|
683
|
+
return null;
|
|
684
|
+
}
|
|
685
|
+
const parsed = Number.parseInt(hostPortRaw, 10);
|
|
686
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
687
|
+
}
|
|
688
|
+
async checkReadyEndpoint(hostPort) {
|
|
689
|
+
const controller = new AbortController();
|
|
690
|
+
const timeoutId = setTimeout(() => controller.abort(), HEALTH_REQUEST_TIMEOUT_MS);
|
|
691
|
+
try {
|
|
692
|
+
const response = await fetch(`http://127.0.0.1:${hostPort}/health/ready`, {
|
|
693
|
+
signal: controller.signal,
|
|
694
|
+
});
|
|
695
|
+
return response.status === 200;
|
|
696
|
+
}
|
|
697
|
+
catch {
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
finally {
|
|
701
|
+
clearTimeout(timeoutId);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
async readStreamFully(stream) {
|
|
705
|
+
return new Promise((resolve, reject) => {
|
|
706
|
+
const chunks = [];
|
|
707
|
+
stream.on('data', (chunk) => {
|
|
708
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
709
|
+
});
|
|
710
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)));
|
|
711
|
+
stream.on('error', reject);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
decodeDockerMultiplexStream(buffer) {
|
|
715
|
+
if (buffer.length < 8) {
|
|
716
|
+
return buffer.toString('utf8');
|
|
717
|
+
}
|
|
718
|
+
const chunks = [];
|
|
719
|
+
let offset = 0;
|
|
720
|
+
while (offset + 8 <= buffer.length) {
|
|
721
|
+
const payloadLength = buffer.readUInt32BE(offset + 4);
|
|
722
|
+
const payloadStart = offset + 8;
|
|
723
|
+
const payloadEnd = payloadStart + payloadLength;
|
|
724
|
+
if (payloadLength < 0 || payloadEnd > buffer.length) {
|
|
725
|
+
return buffer.toString('utf8');
|
|
726
|
+
}
|
|
727
|
+
chunks.push(buffer.subarray(payloadStart, payloadEnd).toString('utf8'));
|
|
728
|
+
offset = payloadEnd;
|
|
729
|
+
}
|
|
730
|
+
if (offset !== buffer.length) {
|
|
731
|
+
return buffer.toString('utf8');
|
|
732
|
+
}
|
|
733
|
+
return chunks.join('');
|
|
734
|
+
}
|
|
735
|
+
sleep(ms) {
|
|
736
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
exports.OrchestratorDockerService = OrchestratorDockerService;
|
|
740
|
+
exports.OrchestratorDockerService = OrchestratorDockerService = __decorate([
|
|
741
|
+
(0, common_1.Injectable)(),
|
|
742
|
+
__param(0, (0, common_1.Optional)()),
|
|
743
|
+
__metadata("design:paramtypes", [Dockerode])
|
|
744
|
+
], OrchestratorDockerService);
|
|
745
|
+
//# sourceMappingURL=docker.service.js.map
|