devchain-cli 0.24.1 → 0.24.2
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/build-info.json +2 -2
- package/dist/cli.js +40 -10
- package/dist/host-install/devchain-host-bootstrap.tgz +0 -0
- package/dist/host-install/pins.json +31 -2
- package/dist/lib/exclusive-run.js +51 -3
- package/dist/lib/git-owner.js +284 -0
- package/dist/lib/host-install.js +19 -151
- package/dist/lib/local-api.js +175 -0
- package/dist/server/common/config/env.config.d.ts +3 -0
- package/dist/server/common/config/env.config.js +1 -0
- package/dist/server/common/config/env.config.js.map +1 -1
- package/dist/server/common/config/host-image.json +3 -3
- package/dist/server/common/constants/syncthing-markers.d.ts +1 -0
- package/dist/server/common/constants/syncthing-markers.js +6 -1
- package/dist/server/common/constants/syncthing-markers.js.map +1 -1
- package/dist/server/common/filesystem/vm-home-path.d.ts +11 -0
- package/dist/server/common/filesystem/vm-home-path.js +48 -0
- package/dist/server/common/filesystem/vm-home-path.js.map +1 -0
- package/dist/server/common/process-ids-env.d.ts +1 -0
- package/dist/server/common/process-ids-env.js +13 -0
- package/dist/server/common/process-ids-env.js.map +1 -0
- package/dist/server/common/test/api-test-app.d.ts +12 -0
- package/dist/server/common/test/api-test-app.js +76 -0
- package/dist/server/common/test/api-test-app.js.map +1 -0
- package/dist/server/common/test/app-bootstrap.helper.js +8 -1
- package/dist/server/common/test/app-bootstrap.helper.js.map +1 -1
- package/dist/server/common/test/fake-bootstrap.server.js +3 -2
- package/dist/server/common/test/fake-bootstrap.server.js.map +1 -1
- package/dist/server/common/test/fake-docker-engine.server.d.ts +17 -6
- package/dist/server/common/test/fake-docker-engine.server.js +77 -33
- package/dist/server/common/test/fake-docker-engine.server.js.map +1 -1
- package/dist/server/common/test/fake-proxmox.server.js +4 -4
- package/dist/server/common/test/fake-ssh.server.js +6 -6
- package/dist/server/common/test/fake-ssh.server.js.map +1 -1
- package/dist/server/common/test/test-database.helper.d.ts +6 -0
- package/dist/server/common/test/test-database.helper.js +29 -0
- package/dist/server/common/test/test-database.helper.js.map +1 -0
- package/dist/server/modules/core/controllers/docker-archive.js +1 -1
- package/dist/server/modules/core/controllers/docker-archive.js.map +1 -1
- package/dist/server/modules/core/controllers/docker-engine.client.d.ts +2 -1
- package/dist/server/modules/core/controllers/docker-engine.client.js +8 -4
- package/dist/server/modules/core/controllers/docker-engine.client.js.map +1 -1
- package/dist/server/modules/core/controllers/docker-image-metadata.d.ts +248 -0
- package/dist/server/modules/core/controllers/docker-image-metadata.js +161 -0
- package/dist/server/modules/core/controllers/docker-image-metadata.js.map +1 -0
- package/dist/server/modules/core/controllers/docker-settings.d.ts +13 -2
- package/dist/server/modules/core/controllers/docker-settings.js +49 -11
- package/dist/server/modules/core/controllers/docker-settings.js.map +1 -1
- package/dist/server/modules/core/controllers/host-user-identity.d.ts +34 -0
- package/dist/server/modules/core/controllers/host-user-identity.js +23 -0
- package/dist/server/modules/core/controllers/host-user-identity.js.map +1 -0
- package/dist/server/modules/core/controllers/runtime.controller.d.ts +11 -4
- package/dist/server/modules/core/controllers/runtime.controller.js +2 -0
- package/dist/server/modules/core/controllers/runtime.controller.js.map +1 -1
- package/dist/server/modules/file-sync/build-exclusion-suggestions.d.ts +15 -0
- package/dist/server/modules/file-sync/build-exclusion-suggestions.js +402 -0
- package/dist/server/modules/file-sync/build-exclusion-suggestions.js.map +1 -0
- package/dist/server/modules/file-sync/file-sync-auto-fix.dto.d.ts +102 -0
- package/dist/server/modules/file-sync/file-sync-auto-fix.dto.js +22 -0
- package/dist/server/modules/file-sync/file-sync-auto-fix.dto.js.map +1 -0
- package/dist/server/modules/file-sync/file-sync-auto-fix.store.d.ts +12 -0
- package/dist/server/modules/file-sync/file-sync-auto-fix.store.js +73 -0
- package/dist/server/modules/file-sync/file-sync-auto-fix.store.js.map +1 -0
- package/dist/server/modules/file-sync/file-sync-conflicts.d.ts +3 -0
- package/dist/server/modules/file-sync/file-sync-conflicts.js +191 -0
- package/dist/server/modules/file-sync/file-sync-conflicts.js.map +1 -0
- package/dist/server/modules/file-sync/file-sync-ignores.store.d.ts +5 -1
- package/dist/server/modules/file-sync/file-sync-ignores.store.js +39 -8
- package/dist/server/modules/file-sync/file-sync-ignores.store.js.map +1 -1
- package/dist/server/modules/file-sync/file-sync.controller.d.ts +1 -3
- package/dist/server/modules/file-sync/file-sync.controller.js +5 -14
- package/dist/server/modules/file-sync/file-sync.controller.js.map +1 -1
- package/dist/server/modules/file-sync/file-sync.dto.d.ts +220 -1
- package/dist/server/modules/file-sync/file-sync.dto.js +70 -4
- package/dist/server/modules/file-sync/file-sync.dto.js.map +1 -1
- package/dist/server/modules/file-sync/file-sync.module.js +16 -1
- package/dist/server/modules/file-sync/file-sync.module.js.map +1 -1
- package/dist/server/modules/file-sync/file-sync.service.d.ts +18 -4
- package/dist/server/modules/file-sync/file-sync.service.js +166 -34
- package/dist/server/modules/file-sync/file-sync.service.js.map +1 -1
- package/dist/server/modules/file-sync/git-guard.dto.d.ts +61 -0
- package/dist/server/modules/file-sync/git-guard.dto.js +26 -0
- package/dist/server/modules/file-sync/git-guard.dto.js.map +1 -0
- package/dist/server/modules/file-sync/home-git-guard.service.d.ts +4 -2
- package/dist/server/modules/file-sync/home-git-guard.service.js +50 -25
- package/dist/server/modules/file-sync/home-git-guard.service.js.map +1 -1
- package/dist/server/modules/file-sync/host-git-guard.controller.d.ts +10 -0
- package/dist/server/modules/file-sync/host-git-guard.controller.js +64 -0
- package/dist/server/modules/file-sync/host-git-guard.controller.js.map +1 -0
- package/dist/server/modules/file-sync/host-git-index.controller.d.ts +9 -0
- package/dist/server/modules/file-sync/host-git-index.controller.js +67 -0
- package/dist/server/modules/file-sync/host-git-index.controller.js.map +1 -0
- package/dist/server/modules/file-sync/host-sync.controller.d.ts +15 -2
- package/dist/server/modules/file-sync/host-sync.controller.js +122 -14
- package/dist/server/modules/file-sync/host-sync.controller.js.map +1 -1
- package/dist/server/modules/file-sync/ignore-pattern-matcher.d.ts +30 -0
- package/dist/server/modules/file-sync/ignore-pattern-matcher.js +183 -0
- package/dist/server/modules/file-sync/ignore-pattern-matcher.js.map +1 -0
- package/dist/server/modules/file-sync/project-repository.d.ts +2 -0
- package/dist/server/modules/file-sync/project-repository.js +20 -0
- package/dist/server/modules/file-sync/project-repository.js.map +1 -0
- package/dist/server/modules/file-sync/sync-chown.dto.d.ts +99 -0
- package/dist/server/modules/file-sync/sync-chown.dto.js +46 -0
- package/dist/server/modules/file-sync/sync-chown.dto.js.map +1 -0
- package/dist/server/modules/file-sync/sync-chown.service.d.ts +12 -0
- package/dist/server/modules/file-sync/sync-chown.service.js +230 -0
- package/dist/server/modules/file-sync/sync-chown.service.js.map +1 -0
- package/dist/server/modules/file-sync/sync-path-inspection.dto.d.ts +626 -0
- package/dist/server/modules/file-sync/sync-path-inspection.dto.js +108 -0
- package/dist/server/modules/file-sync/sync-path-inspection.dto.js.map +1 -0
- package/dist/server/modules/file-sync/sync-path-inspector.d.ts +12 -0
- package/dist/server/modules/file-sync/sync-path-inspector.js +413 -0
- package/dist/server/modules/file-sync/sync-path-inspector.js.map +1 -0
- package/dist/server/modules/file-sync/testing/fake-file-sync.service.d.ts +27 -4
- package/dist/server/modules/file-sync/testing/fake-file-sync.service.js +66 -4
- package/dist/server/modules/file-sync/testing/fake-file-sync.service.js.map +1 -1
- package/dist/server/modules/file-sync/translate-git-ignore-rule.d.ts +3 -0
- package/dist/server/modules/file-sync/translate-git-ignore-rule.js +30 -0
- package/dist/server/modules/file-sync/translate-git-ignore-rule.js.map +1 -0
- package/dist/server/modules/mcp/services/handlers/skill-tools.js +2 -2
- package/dist/server/modules/mcp/services/handlers/skill-tools.js.map +1 -1
- package/dist/server/modules/remotes/connect-choices.controller.d.ts +9 -0
- package/dist/server/modules/remotes/connect-choices.controller.js +54 -0
- package/dist/server/modules/remotes/connect-choices.controller.js.map +1 -0
- package/dist/server/modules/remotes/connect-choices.dto.d.ts +52 -0
- package/dist/server/modules/remotes/connect-choices.dto.js +19 -0
- package/dist/server/modules/remotes/connect-choices.dto.js.map +1 -0
- package/dist/server/modules/remotes/connect-choices.store.d.ts +12 -0
- package/dist/server/modules/remotes/connect-choices.store.js +95 -0
- package/dist/server/modules/remotes/connect-choices.store.js.map +1 -0
- package/dist/server/modules/remotes/controllers/remotes.controller.d.ts +3 -0
- package/dist/server/modules/remotes/controllers/remotes.controller.js +9 -1
- package/dist/server/modules/remotes/controllers/remotes.controller.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-copy-back.js +40 -16
- package/dist/server/modules/remotes/docker/docker-copy-back.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-handoff.d.ts +5 -3
- package/dist/server/modules/remotes/docker/docker-handoff.js +146 -42
- package/dist/server/modules/remotes/docker/docker-handoff.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-handoff.store.d.ts +3 -0
- package/dist/server/modules/remotes/docker/docker-handoff.store.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-plan-fit.d.ts +4 -1
- package/dist/server/modules/remotes/docker/docker-plan-fit.js +19 -6
- package/dist/server/modules/remotes/docker/docker-plan-fit.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-plan-policy.d.ts +6 -2
- package/dist/server/modules/remotes/docker/docker-plan-policy.js +191 -38
- package/dist/server/modules/remotes/docker/docker-plan-policy.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-plan-source.service.d.ts +52 -8
- package/dist/server/modules/remotes/docker/docker-plan-source.service.js +273 -50
- package/dist/server/modules/remotes/docker/docker-plan-source.service.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-plan.controller.d.ts +2 -1
- package/dist/server/modules/remotes/docker/docker-plan.controller.js +14 -0
- package/dist/server/modules/remotes/docker/docker-plan.controller.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-plan.dto.d.ts +46 -1
- package/dist/server/modules/remotes/docker/docker-plan.dto.js +1 -0
- package/dist/server/modules/remotes/docker/docker-plan.dto.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-plan.service.d.ts +13 -7
- package/dist/server/modules/remotes/docker/docker-plan.service.js +57 -13
- package/dist/server/modules/remotes/docker/docker-plan.service.js.map +1 -1
- package/dist/server/modules/remotes/docker/docker-project-compose.d.ts +7 -0
- package/dist/server/modules/remotes/docker/docker-project-compose.js +30 -0
- package/dist/server/modules/remotes/docker/docker-project-compose.js.map +1 -0
- package/dist/server/modules/remotes/dtos/remote.dto.d.ts +2 -0
- package/dist/server/modules/remotes/dtos/remote.dto.js.map +1 -1
- package/dist/server/modules/remotes/git-owner.store.d.ts +13 -0
- package/dist/server/modules/remotes/git-owner.store.js +69 -0
- package/dist/server/modules/remotes/git-owner.store.js.map +1 -0
- package/dist/server/modules/remotes/host/host-docker.controller.d.ts +8 -0
- package/dist/server/modules/remotes/host/host-docker.controller.js +22 -6
- package/dist/server/modules/remotes/host/host-docker.controller.js.map +1 -1
- package/dist/server/modules/remotes/host/host-docker.dto.d.ts +606 -0
- package/dist/server/modules/remotes/host/host-docker.dto.js +47 -7
- package/dist/server/modules/remotes/host/host-docker.dto.js.map +1 -1
- package/dist/server/modules/remotes/host/host-docker.service.d.ts +16 -4
- package/dist/server/modules/remotes/host/host-docker.service.js +157 -82
- package/dist/server/modules/remotes/host/host-docker.service.js.map +1 -1
- package/dist/server/modules/remotes/host/host-ipv4-routes.d.ts +4 -0
- package/dist/server/modules/remotes/host/host-ipv4-routes.js +43 -0
- package/dist/server/modules/remotes/host/host-ipv4-routes.js.map +1 -0
- package/dist/server/modules/remotes/host-image.d.ts +1 -1
- package/dist/server/modules/remotes/host-image.js +1 -1
- package/dist/server/modules/remotes/host-install/host-install-block.d.ts +1 -0
- package/dist/server/modules/remotes/host-install/host-install-block.js +76 -2
- package/dist/server/modules/remotes/host-install/host-install-block.js.map +1 -1
- package/dist/server/modules/remotes/host-install/host-install.service.d.ts +0 -1
- package/dist/server/modules/remotes/host-install/host-install.service.js +9 -26
- package/dist/server/modules/remotes/host-install/host-install.service.js.map +1 -1
- package/dist/server/modules/remotes/operations/attach.operation.d.ts +5 -1
- package/dist/server/modules/remotes/operations/attach.operation.js +40 -5
- package/dist/server/modules/remotes/operations/attach.operation.js.map +1 -1
- package/dist/server/modules/remotes/operations/claim.operation.d.ts +1 -0
- package/dist/server/modules/remotes/operations/claim.operation.js +6 -1
- package/dist/server/modules/remotes/operations/claim.operation.js.map +1 -1
- package/dist/server/modules/remotes/operations/detach.operation.d.ts +4 -1
- package/dist/server/modules/remotes/operations/detach.operation.js +10 -2
- package/dist/server/modules/remotes/operations/detach.operation.js.map +1 -1
- package/dist/server/modules/remotes/operations/docker-import-inventory.store.d.ts +4 -0
- package/dist/server/modules/remotes/operations/docker-import-inventory.store.js +11 -0
- package/dist/server/modules/remotes/operations/docker-import-inventory.store.js.map +1 -1
- package/dist/server/modules/remotes/operations/file-sync-handoff.d.ts +29 -3
- package/dist/server/modules/remotes/operations/file-sync-handoff.js +477 -50
- package/dist/server/modules/remotes/operations/file-sync-handoff.js.map +1 -1
- package/dist/server/modules/remotes/operations/force-sync.operation.d.ts +39 -0
- package/dist/server/modules/remotes/operations/force-sync.operation.js +177 -0
- package/dist/server/modules/remotes/operations/force-sync.operation.js.map +1 -0
- package/dist/server/modules/remotes/operations/git-owner.dto.d.ts +83 -0
- package/dist/server/modules/remotes/operations/git-owner.dto.js +25 -0
- package/dist/server/modules/remotes/operations/git-owner.dto.js.map +1 -0
- package/dist/server/modules/remotes/operations/git-owner.operation.d.ts +35 -0
- package/dist/server/modules/remotes/operations/git-owner.operation.js +216 -0
- package/dist/server/modules/remotes/operations/git-owner.operation.js.map +1 -0
- package/dist/server/modules/remotes/operations/remote-host.client.d.ts +71 -7
- package/dist/server/modules/remotes/operations/remote-host.client.js +141 -11
- package/dist/server/modules/remotes/operations/remote-host.client.js.map +1 -1
- package/dist/server/modules/remotes/operations/remote-operation.dto.d.ts +19 -0
- package/dist/server/modules/remotes/operations/remote-operation.dto.js +5 -1
- package/dist/server/modules/remotes/operations/remote-operation.dto.js.map +1 -1
- package/dist/server/modules/remotes/operations/remote-operation.runner.d.ts +3 -1
- package/dist/server/modules/remotes/operations/remote-operation.runner.js +52 -21
- package/dist/server/modules/remotes/operations/remote-operation.runner.js.map +1 -1
- package/dist/server/modules/remotes/operations/remote-operation.types.d.ts +4 -1
- package/dist/server/modules/remotes/operations/remote-operation.types.js.map +1 -1
- package/dist/server/modules/remotes/operations/remote-operations.controller.d.ts +5 -0
- package/dist/server/modules/remotes/operations/remote-operations.controller.js +56 -0
- package/dist/server/modules/remotes/operations/remote-operations.controller.js.map +1 -1
- package/dist/server/modules/remotes/operations/remote-operations.service.d.ts +16 -1
- package/dist/server/modules/remotes/operations/remote-operations.service.js +155 -5
- package/dist/server/modules/remotes/operations/remote-operations.service.js.map +1 -1
- package/dist/server/modules/remotes/operations/update-logins.operation.d.ts +1 -3
- package/dist/server/modules/remotes/operations/update-logins.operation.js +3 -18
- package/dist/server/modules/remotes/operations/update-logins.operation.js.map +1 -1
- package/dist/server/modules/remotes/ports/remote-health.port.d.ts +2 -0
- package/dist/server/modules/remotes/ports/remote-health.port.js.map +1 -1
- package/dist/server/modules/remotes/remotes.module.js +18 -0
- package/dist/server/modules/remotes/remotes.module.js.map +1 -1
- package/dist/server/modules/remotes/replica/__fixtures__/replica-seed.d.ts +2 -2
- package/dist/server/modules/remotes/replica/__fixtures__/replica-seed.js +2 -13
- package/dist/server/modules/remotes/replica/__fixtures__/replica-seed.js.map +1 -1
- package/dist/server/modules/remotes/services/remote-health.service.js +5 -0
- package/dist/server/modules/remotes/services/remote-health.service.js.map +1 -1
- package/dist/server/modules/remotes/sync/file-sync-failures.service.d.ts +18 -0
- package/dist/server/modules/remotes/sync/file-sync-failures.service.js +157 -0
- package/dist/server/modules/remotes/sync/file-sync-failures.service.js.map +1 -0
- package/dist/server/modules/remotes/sync/file-sync-pattern-preview.service.d.ts +14 -0
- package/dist/server/modules/remotes/sync/file-sync-pattern-preview.service.js +99 -0
- package/dist/server/modules/remotes/sync/file-sync-pattern-preview.service.js.map +1 -0
- package/dist/server/modules/remotes/sync/file-sync-suggestions.service.d.ts +15 -0
- package/dist/server/modules/remotes/sync/file-sync-suggestions.service.js +90 -0
- package/dist/server/modules/remotes/sync/file-sync-suggestions.service.js.map +1 -0
- package/dist/server/modules/remotes/sync/project-file-sync.controller.d.ts +29 -0
- package/dist/server/modules/remotes/sync/project-file-sync.controller.js +160 -0
- package/dist/server/modules/remotes/sync/project-file-sync.controller.js.map +1 -0
- package/dist/server/modules/remotes/sync/remote-file-sync.dto.d.ts +72 -0
- package/dist/server/modules/remotes/sync/remote-file-sync.dto.js +16 -0
- package/dist/server/modules/remotes/sync/remote-file-sync.dto.js.map +1 -0
- package/dist/server/modules/remotes/sync/remote-file-sync.service.d.ts +32 -1
- package/dist/server/modules/remotes/sync/remote-file-sync.service.js +499 -38
- package/dist/server/modules/remotes/sync/remote-file-sync.service.js.map +1 -1
- package/dist/server/modules/remotes/sync/remote-live-sync.service.d.ts +2 -0
- package/dist/server/modules/remotes/sync/remote-live-sync.service.js +26 -12
- package/dist/server/modules/remotes/sync/remote-live-sync.service.js.map +1 -1
- package/dist/server/modules/remotes/vm-user-identity.d.ts +29 -0
- package/dist/server/modules/remotes/vm-user-identity.js +45 -0
- package/dist/server/modules/remotes/vm-user-identity.js.map +1 -0
- package/dist/server/modules/runtime-context-capture/codex-plugin-profile-materializer.service.js +5 -1
- package/dist/server/modules/runtime-context-capture/codex-plugin-profile-materializer.service.js.map +1 -1
- package/dist/server/modules/session-reader/data/pricing.json +55 -7
- package/dist/server/modules/sessions/services/provider-launch-config/provider-launch-config.service.js +5 -0
- package/dist/server/modules/sessions/services/provider-launch-config/provider-launch-config.service.js.map +1 -1
- package/dist/server/modules/settings/local/delegates/skills-settings.delegate.d.ts +6 -0
- package/dist/server/modules/settings/local/delegates/skills-settings.delegate.js +26 -0
- package/dist/server/modules/settings/local/delegates/skills-settings.delegate.js.map +1 -1
- package/dist/server/modules/settings/services/settings.service.d.ts +3 -1
- package/dist/server/modules/settings/services/settings.service.js +6 -0
- package/dist/server/modules/settings/services/settings.service.js.map +1 -1
- package/dist/server/modules/skills/adapters/anthropic-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/anthropic-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/adapters/github-directory-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/github-directory-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/adapters/github-skill-source.base.js +4 -3
- package/dist/server/modules/skills/adapters/github-skill-source.base.js.map +1 -1
- package/dist/server/modules/skills/adapters/local-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/local-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/adapters/microsoft-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/microsoft-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/adapters/openai-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/openai-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/adapters/skill-source.adapter.d.ts +5 -0
- package/dist/server/modules/skills/adapters/skill-sync-temp-cleanup.d.ts +5 -0
- package/dist/server/modules/skills/adapters/skill-sync-temp-cleanup.js +75 -0
- package/dist/server/modules/skills/adapters/skill-sync-temp-cleanup.js.map +1 -0
- package/dist/server/modules/skills/adapters/trailofbits-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/trailofbits-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/adapters/vercel-skill-source.adapter.js +5 -5
- package/dist/server/modules/skills/adapters/vercel-skill-source.adapter.js.map +1 -1
- package/dist/server/modules/skills/controllers/skills.controller.js +2 -2
- package/dist/server/modules/skills/controllers/skills.controller.js.map +1 -1
- package/dist/server/modules/skills/services/skill-source-lifecycle.service.d.ts +3 -3
- package/dist/server/modules/skills/services/skill-source-lifecycle.service.js +9 -4
- package/dist/server/modules/skills/services/skill-source-lifecycle.service.js.map +1 -1
- package/dist/server/modules/skills/services/skill-sync.service.d.ts +3 -3
- package/dist/server/modules/skills/services/skill-sync.service.js +32 -6
- package/dist/server/modules/skills/services/skill-sync.service.js.map +1 -1
- package/dist/server/modules/skills/services/skill-sync.types.d.ts +3 -0
- package/dist/server/modules/storage/db/schema.d.ts +2 -2
- package/dist/server/modules/storage/db/schema.js +2 -0
- package/dist/server/modules/storage/db/schema.js.map +1 -1
- package/dist/server/modules/storage/models/domain.models.d.ts +1 -1
- package/dist/server/modules/storage/models/domain.models.js +2 -0
- package/dist/server/modules/storage/models/domain.models.js.map +1 -1
- package/dist/server/modules/terminal/gateways/terminal.gateway.d.ts +3 -1
- package/dist/server/modules/terminal/gateways/terminal.gateway.js +9 -4
- package/dist/server/modules/terminal/gateways/terminal.gateway.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-activity.service.d.ts +0 -1
- package/dist/server/modules/terminal/services/terminal-activity.service.js +0 -3
- package/dist/server/modules/terminal/services/terminal-activity.service.js.map +1 -1
- package/dist/server/modules/terminal/services/terminal-io/lifecycle.js +15 -3
- package/dist/server/modules/terminal/services/terminal-io/lifecycle.js.map +1 -1
- package/dist/server/modules/terminal/utils/ansi-sanitizer.d.ts +0 -1
- package/dist/server/modules/terminal/utils/ansi-sanitizer.js +0 -4
- package/dist/server/modules/terminal/utils/ansi-sanitizer.js.map +1 -1
- package/dist/server/templates/teams-dev.json +11 -11
- package/dist/server/test-setup-node.js +4 -3
- package/dist/server/test-setup-node.js.map +1 -1
- 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/{ExternalRichEditor-DtNvrV0F.js → ExternalRichEditor-CIZPhssf.js} +24 -24
- package/dist/server/ui/assets/ReviewDetailPage-winyLSQ7.js +6 -0
- package/dist/server/ui/assets/ReviewsPage-BYKBReMp.js +6 -0
- package/dist/server/ui/assets/index-CiDJX6Hn.js +1191 -0
- package/dist/server/ui/assets/index-f1WOAqXB.css +32 -0
- package/dist/server/ui/assets/useReviewSubscription-v3Cto0y4.js +69 -0
- package/dist/server/ui/index.html +2 -2
- package/dist/templates/teams-dev.json +11 -11
- package/package.json +8 -1
- package/dist/server/modules/reviews/utils/notification-formatter.d.ts +0 -12
- package/dist/server/modules/reviews/utils/notification-formatter.js +0 -60
- package/dist/server/modules/reviews/utils/notification-formatter.js.map +0 -1
- package/dist/server/ui/assets/ReviewDetailPage-DJzEoM0x.js +0 -6
- package/dist/server/ui/assets/ReviewsPage-z2O8MNEw.js +0 -6
- package/dist/server/ui/assets/index-CC4P3V8L.js +0 -994
- package/dist/server/ui/assets/index-LsJe1ZTS.css +0 -32
- package/dist/server/ui/assets/useReviewSubscription-B7AdJBO2.js +0 -69
|
@@ -14,19 +14,32 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.RemoteFileSyncService = void 0;
|
|
16
16
|
const common_1 = require("@nestjs/common");
|
|
17
|
+
const node_os_1 = require("node:os");
|
|
18
|
+
const git_owner_store_1 = require("../git-owner.store");
|
|
17
19
|
const logger_1 = require("../../../common/logging/logger");
|
|
20
|
+
const error_types_1 = require("../../../common/errors/error-types");
|
|
18
21
|
const home_git_guard_service_1 = require("../../file-sync/home-git-guard.service");
|
|
19
22
|
const file_sync_managed_exclusions_store_1 = require("../../file-sync/file-sync-managed-exclusions.store");
|
|
23
|
+
const file_sync_auto_fix_store_1 = require("../../file-sync/file-sync-auto-fix.store");
|
|
24
|
+
const sync_path_inspection_dto_1 = require("../../file-sync/sync-path-inspection.dto");
|
|
25
|
+
const sync_chown_dto_1 = require("../../file-sync/sync-chown.dto");
|
|
26
|
+
const file_sync_failures_service_1 = require("./file-sync-failures.service");
|
|
20
27
|
const file_sync_dto_1 = require("../../file-sync/file-sync.dto");
|
|
28
|
+
const syncthing_rest_client_1 = require("../../file-sync/syncthing-rest.client");
|
|
21
29
|
const file_sync_service_1 = require("../../file-sync/file-sync.service");
|
|
22
30
|
const git_service_1 = require("../../git/services/git.service");
|
|
31
|
+
const storage_interface_1 = require("../../storage/interfaces/storage.interface");
|
|
23
32
|
const remote_host_client_1 = require("../operations/remote-host.client");
|
|
24
33
|
const remote_health_port_1 = require("../ports/remote-health.port");
|
|
25
34
|
const remote_bindings_service_1 = require("../services/remote-bindings.service");
|
|
35
|
+
const remote_file_sync_dto_1 = require("./remote-file-sync.dto");
|
|
26
36
|
const logger = (0, logger_1.createLogger)('RemoteFileSyncService');
|
|
27
37
|
const CHECK_INTERVAL_MS = 30_000;
|
|
28
38
|
const PROBLEM_GRACE_MS = 2 * 60_000;
|
|
29
39
|
const STALLED_MS = 10 * 60_000;
|
|
40
|
+
const REPEAT_REPAIR_MS = 60 * 60_000;
|
|
41
|
+
const MOVE_STEPS = ['create', 'settle', 'ignores', 'directions', 'guard', 'done'];
|
|
42
|
+
const stepIndex = (step) => MOVE_STEPS.indexOf(step);
|
|
30
43
|
let RemoteFileSyncService = class RemoteFileSyncService {
|
|
31
44
|
files;
|
|
32
45
|
host;
|
|
@@ -35,8 +48,12 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
35
48
|
exclusions;
|
|
36
49
|
git;
|
|
37
50
|
guard;
|
|
51
|
+
storage;
|
|
52
|
+
autoFix;
|
|
53
|
+
failures;
|
|
54
|
+
gitOwner;
|
|
38
55
|
projects = new Map();
|
|
39
|
-
constructor(files, host, bindings, health, exclusions, git, guard) {
|
|
56
|
+
constructor(files, host, bindings, health, exclusions, git, guard, storage, autoFix, failures, gitOwner) {
|
|
40
57
|
this.files = files;
|
|
41
58
|
this.host = host;
|
|
42
59
|
this.bindings = bindings;
|
|
@@ -44,18 +61,286 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
44
61
|
this.exclusions = exclusions;
|
|
45
62
|
this.git = git;
|
|
46
63
|
this.guard = guard;
|
|
64
|
+
this.storage = storage;
|
|
65
|
+
this.autoFix = autoFix;
|
|
66
|
+
this.failures = failures;
|
|
67
|
+
this.gitOwner = gitOwner;
|
|
68
|
+
}
|
|
69
|
+
getIgnores(projectId) {
|
|
70
|
+
return {
|
|
71
|
+
ignores: this.files.getIgnores(projectId),
|
|
72
|
+
revision: this.files.getIgnoresRevision(projectId),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
getAutoFix(projectId) {
|
|
76
|
+
return this.autoFix.get(projectId);
|
|
77
|
+
}
|
|
78
|
+
setAutoFix(projectId, enabled) {
|
|
79
|
+
return this.autoFix.setEnabled(projectId, enabled);
|
|
80
|
+
}
|
|
81
|
+
ownershipNotes(projectId) {
|
|
82
|
+
const notes = this.projects.get(projectId)?.ownershipNotes;
|
|
83
|
+
if (!notes)
|
|
84
|
+
return [];
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
for (const [path, at] of notes)
|
|
87
|
+
if (now - at >= REPEAT_REPAIR_MS)
|
|
88
|
+
notes.delete(path);
|
|
89
|
+
return [...notes.keys()].map((path) => `A container keeps writing as another user in ${path}.`);
|
|
90
|
+
}
|
|
91
|
+
async giveOwnership(projectId, paths, active) {
|
|
92
|
+
await this.assertNoHold(projectId);
|
|
93
|
+
const binding = await this.bindings.get(projectId);
|
|
94
|
+
if (binding?.state !== 'remote' || !active())
|
|
95
|
+
throw new error_types_1.ConflictError('The project must be connected to repair VM owners.');
|
|
96
|
+
const health = this.health.getState(binding.remoteId);
|
|
97
|
+
if (!health.online || health.apiKeyRejected || !health.versionMatches)
|
|
98
|
+
throw new error_types_1.ConflictError('The VM must be online with an accepted key and matching version.');
|
|
99
|
+
const failed = await this.failures.failed(projectId);
|
|
100
|
+
const eligible = new Set(failed.groups.filter(sync_path_inspection_dto_1.isGiveOwnershipEligible).map((group) => group.path));
|
|
101
|
+
const accepted = [...new Set(paths)].filter((path) => eligible.has(path));
|
|
102
|
+
const refused = paths
|
|
103
|
+
.filter((path) => !eligible.has(path))
|
|
104
|
+
.map((path) => ({
|
|
105
|
+
path,
|
|
106
|
+
state: 'refused',
|
|
107
|
+
paths: [],
|
|
108
|
+
reason: 'Give to requires a foreign-owned VM path Git neither tracks nor ignores.',
|
|
109
|
+
}));
|
|
110
|
+
if (!accepted.length)
|
|
111
|
+
return { user: failed.vmUser ?? null, items: refused };
|
|
112
|
+
await this.assertNoHold(projectId);
|
|
113
|
+
if (!active())
|
|
114
|
+
throw new error_types_1.ConflictError('The connection is changing.');
|
|
115
|
+
const { rootPath } = await this.storage.getProject(projectId);
|
|
116
|
+
const result = await this.host.syncChown(binding.remoteId, {
|
|
117
|
+
root: rootPath,
|
|
118
|
+
items: accepted.map((path) => ({ path, mode: 'give' })),
|
|
119
|
+
});
|
|
120
|
+
this.recordRepairs(projectId, result, Date.now());
|
|
121
|
+
if (result.items.some((item) => item.paths.length))
|
|
122
|
+
await this.retryPullErrors(projectId, 'vm', binding.remoteId, active);
|
|
123
|
+
return { ...result, items: [...result.items, ...refused] };
|
|
124
|
+
}
|
|
125
|
+
recordRepairs(projectId, result, now) {
|
|
126
|
+
const paths = [...new Set(result.items.flatMap((item) => item.paths))];
|
|
127
|
+
for (let offset = 0; offset < paths.length; offset += sync_chown_dto_1.SYNC_CHOWN_PATHS_MAX)
|
|
128
|
+
this.autoFix.record(projectId, {
|
|
129
|
+
at: new Date(now).toISOString(),
|
|
130
|
+
kind: 'chown',
|
|
131
|
+
side: 'vm',
|
|
132
|
+
paths: paths.slice(offset, offset + sync_chown_dto_1.SYNC_CHOWN_PATHS_MAX),
|
|
133
|
+
});
|
|
47
134
|
}
|
|
48
135
|
warning(projectId) {
|
|
49
136
|
const state = this.projects.get(projectId);
|
|
50
137
|
return state?.checkWarning ?? state?.warning ?? state?.guardWarning ?? null;
|
|
51
138
|
}
|
|
139
|
+
problem(projectId) {
|
|
140
|
+
const state = this.projects.get(projectId);
|
|
141
|
+
if (state?.checkWarning)
|
|
142
|
+
return state.checkProblem ?? null;
|
|
143
|
+
if (state?.warning)
|
|
144
|
+
return 'setup';
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
async hasOpenHold(projectId) {
|
|
148
|
+
const operations = await this.storage.listRemoteOperations({
|
|
149
|
+
projectId,
|
|
150
|
+
kinds: ['force_sync', 'git_owner'],
|
|
151
|
+
states: ['running', 'failed'],
|
|
152
|
+
limit: 1,
|
|
153
|
+
});
|
|
154
|
+
return operations.some((operation) => (operation.kind === 'force_sync' || operation.kind === 'git_owner') &&
|
|
155
|
+
(operation.state === 'running' || operation.state === 'failed'));
|
|
156
|
+
}
|
|
157
|
+
async assertNoHold(projectId) {
|
|
158
|
+
if (await this.hasOpenHold(projectId))
|
|
159
|
+
throw new error_types_1.ConflictError('Force sync or a Git switch is running for this project. Save the list after it finishes.');
|
|
160
|
+
}
|
|
161
|
+
async forceSyncOffer(projectId, excludeOperationId) {
|
|
162
|
+
const pending = { fromVm: null, fromHome: null };
|
|
163
|
+
const refuse = (reason) => ({ offered: false, reason, pending });
|
|
164
|
+
const binding = await this.bindings.get(projectId);
|
|
165
|
+
if (binding?.state !== 'remote')
|
|
166
|
+
return refuse('The project must be connected to Force sync.');
|
|
167
|
+
const device = await this.host.syncDevice(binding.remoteId).catch(() => null);
|
|
168
|
+
const ids = ['code', 'git'].map((kind) => (0, file_sync_service_1.projectFolderId)(projectId, kind));
|
|
169
|
+
const reads = await Promise.allSettled([
|
|
170
|
+
...ids.map((id, index) => this.files.status(id, index === 0 ? device?.deviceId : undefined, { allErrors: true })),
|
|
171
|
+
...ids.map((id) => this.host.syncStatus(binding.remoteId, id, undefined, { allErrors: true })),
|
|
172
|
+
]);
|
|
173
|
+
const homeCode = reads[0];
|
|
174
|
+
if (homeCode.status === 'fulfilled') {
|
|
175
|
+
pending.fromVm = homeCode.value.needTotalItems ?? null;
|
|
176
|
+
pending.fromHome = homeCode.value.peer?.needItems ?? null;
|
|
177
|
+
}
|
|
178
|
+
const errors = [];
|
|
179
|
+
for (const [index, read] of reads.entries()) {
|
|
180
|
+
if (read.status === 'rejected') {
|
|
181
|
+
const error = read.reason;
|
|
182
|
+
if ((error instanceof syncthing_rest_client_1.SyncthingRestError || error instanceof remote_host_client_1.RemoteHostRequestError) &&
|
|
183
|
+
error.status === 404)
|
|
184
|
+
continue;
|
|
185
|
+
return refuse(index < 2
|
|
186
|
+
? "DevChain could not read this PC's file sync status."
|
|
187
|
+
: "DevChain could not read the VM's file sync status.");
|
|
188
|
+
}
|
|
189
|
+
const status = read.value;
|
|
190
|
+
errors.push(...(status.fileErrors ?? []).map((entry) => entry.error));
|
|
191
|
+
if (status.error)
|
|
192
|
+
errors.push(status.error);
|
|
193
|
+
}
|
|
194
|
+
const persistent = errors.filter((error) => !(0, file_sync_dto_1.isTransientSyncError)(error));
|
|
195
|
+
if (persistent.some((error) => /permission denied|operation not permitted/i.test(error)))
|
|
196
|
+
return refuse('Repair file permissions before using Force sync.');
|
|
197
|
+
const health = this.health.getState(binding.remoteId);
|
|
198
|
+
if (health.apiKeyRejected)
|
|
199
|
+
return refuse("The VM rejected this PC's API key.");
|
|
200
|
+
if (!health.online)
|
|
201
|
+
return refuse('The VM is offline.');
|
|
202
|
+
if (!health.versionMatches)
|
|
203
|
+
return refuse('The VM runs a different DevChain version.');
|
|
204
|
+
if (!device)
|
|
205
|
+
return refuse("DevChain could not read the VM's file sync status.");
|
|
206
|
+
const connected = await this.files.isConnected(device.deviceId).catch(() => false);
|
|
207
|
+
if (!connected)
|
|
208
|
+
return refuse('There is no file sync connection to the VM.');
|
|
209
|
+
const operations = await this.storage.listRemoteOperations({
|
|
210
|
+
projectId,
|
|
211
|
+
states: ['running', 'failed'],
|
|
212
|
+
});
|
|
213
|
+
if (operations.some((operation) => operation.id !== excludeOperationId))
|
|
214
|
+
return refuse('Another operation is open for this project.');
|
|
215
|
+
const problem = this.problem(projectId);
|
|
216
|
+
const failedFiles = reads.some((read) => read.status === 'fulfilled' &&
|
|
217
|
+
(read.value.fileErrors ?? []).some((entry) => !(0, file_sync_dto_1.isTransientSyncError)(entry.error)));
|
|
218
|
+
if (!(0, remote_file_sync_dto_1.isStuckFileSyncProblem)(problem) && !failedFiles)
|
|
219
|
+
return refuse('File sync has no stuck problem to repair.');
|
|
220
|
+
return { offered: true, reason: null, pending };
|
|
221
|
+
}
|
|
222
|
+
failedCounts(projectId) {
|
|
223
|
+
return this.projects.get(projectId)?.failed ?? null;
|
|
224
|
+
}
|
|
52
225
|
forget(projectId) {
|
|
53
226
|
this.projects.delete(projectId);
|
|
54
227
|
}
|
|
228
|
+
async saveIgnores(projectId, list, active, expectedRevision) {
|
|
229
|
+
const ignores = this.files.setIgnores(projectId, list, expectedRevision);
|
|
230
|
+
const revision = this.files.getIgnoresRevision(projectId);
|
|
231
|
+
const notApplied = (reason) => ({
|
|
232
|
+
ignores,
|
|
233
|
+
revision,
|
|
234
|
+
applied: false,
|
|
235
|
+
message: `Saved, not applied yet: ${reason}; DevChain applies it automatically while the project stays connected.`,
|
|
236
|
+
});
|
|
237
|
+
const deferred = (reason) => {
|
|
238
|
+
const state = this.projects.get(projectId);
|
|
239
|
+
if (state && stepIndex(state.step) > stepIndex('ignores'))
|
|
240
|
+
state.step = 'ignores';
|
|
241
|
+
return notApplied(reason);
|
|
242
|
+
};
|
|
243
|
+
let reason = 'the connection could not be checked';
|
|
244
|
+
try {
|
|
245
|
+
const binding = await this.bindings.get(projectId);
|
|
246
|
+
if (binding?.state === 'attaching')
|
|
247
|
+
return notApplied('the project is connecting');
|
|
248
|
+
if (binding?.state !== 'remote') {
|
|
249
|
+
return {
|
|
250
|
+
ignores,
|
|
251
|
+
revision,
|
|
252
|
+
applied: false,
|
|
253
|
+
message: 'Saved. The list applies at the next Connect.',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
if (!active())
|
|
257
|
+
return deferred('live sync is not running');
|
|
258
|
+
const health = this.health.getState(binding.remoteId);
|
|
259
|
+
if (health.apiKeyRejected)
|
|
260
|
+
return deferred("the VM rejected this PC's API key");
|
|
261
|
+
if (!health.online)
|
|
262
|
+
return deferred('the VM is offline');
|
|
263
|
+
if (!health.versionMatches)
|
|
264
|
+
return deferred('the VM runs a different DevChain version');
|
|
265
|
+
const state = this.projects.get(projectId);
|
|
266
|
+
if (!state ||
|
|
267
|
+
state.remoteId !== binding.remoteId ||
|
|
268
|
+
stepIndex(state.step) < stepIndex('ignores')) {
|
|
269
|
+
return deferred('file sync setup is still running');
|
|
270
|
+
}
|
|
271
|
+
reason = 'the ignore list could not be prepared';
|
|
272
|
+
const installed = (0, file_sync_dto_1.codeIgnores)(this.exclusions.get(projectId), ignores);
|
|
273
|
+
const codeId = (0, file_sync_service_1.projectFolderId)(projectId, 'code');
|
|
274
|
+
reason = 'the VM could not apply the list';
|
|
275
|
+
await this.host.syncFolderType(binding.remoteId, codeId, { ignores: installed });
|
|
276
|
+
await this.retryPullErrors(projectId, 'vm', binding.remoteId, active);
|
|
277
|
+
if (!active())
|
|
278
|
+
return deferred('the connection is changing');
|
|
279
|
+
reason = 'this PC could not apply the list';
|
|
280
|
+
try {
|
|
281
|
+
await this.files.updateFolder(codeId, { ignores: installed });
|
|
282
|
+
await this.retryPullErrors(projectId, 'home', binding.remoteId, active);
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
if (error instanceof syncthing_rest_client_1.SyncthingRestError && error.status === 404)
|
|
286
|
+
reason = 'the folder is not shared on this PC';
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
return { ignores, revision, applied: true, message: 'Applied to the VM and this PC.' };
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
logger.warn({ error, projectId }, 'Saved file sync ignores will retry');
|
|
293
|
+
return deferred(reason);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async retryPullErrors(projectId, side, remoteId, active) {
|
|
297
|
+
const codeId = (0, file_sync_service_1.projectFolderId)(projectId, 'code');
|
|
298
|
+
try {
|
|
299
|
+
if (!active())
|
|
300
|
+
return;
|
|
301
|
+
const status = side === 'vm'
|
|
302
|
+
? await this.host.syncStatus(remoteId, codeId)
|
|
303
|
+
: await this.files.status(codeId);
|
|
304
|
+
if (!active() || (status.pullErrors ?? 0) <= 0)
|
|
305
|
+
return;
|
|
306
|
+
const state = this.projects.get(projectId);
|
|
307
|
+
if (state)
|
|
308
|
+
(state.resume ??= new Set()).add(side);
|
|
309
|
+
try {
|
|
310
|
+
await this.pauseCode(codeId, side, remoteId, true);
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
await this.pauseCode(codeId, side, remoteId, false);
|
|
314
|
+
state?.resume?.delete(side);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
logger.warn({ error, projectId, side }, 'File sync folder restart failed after applying ignores');
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
pauseCode(codeId, side, remoteId, paused) {
|
|
322
|
+
return side === 'vm'
|
|
323
|
+
? this.host.syncFolderType(remoteId, codeId, { paused })
|
|
324
|
+
: this.files.updateFolder(codeId, { paused });
|
|
325
|
+
}
|
|
326
|
+
async resumeOwed(projectId, state) {
|
|
327
|
+
const codeId = (0, file_sync_service_1.projectFolderId)(projectId, 'code');
|
|
328
|
+
for (const side of [...(state.resume ?? [])]) {
|
|
329
|
+
try {
|
|
330
|
+
await this.pauseCode(codeId, side, state.remoteId, false);
|
|
331
|
+
state.resume.delete(side);
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
logger.warn({ error, projectId, side }, 'File sync folder resume will retry');
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
55
338
|
async tick(projectId, remoteId, active) {
|
|
56
339
|
const health = this.health.getState(remoteId);
|
|
57
340
|
if (!active() || !health.online || health.apiKeyRejected || !health.versionMatches)
|
|
58
341
|
return;
|
|
342
|
+
if (await this.hasOpenHold(projectId))
|
|
343
|
+
return;
|
|
59
344
|
const binding = await this.bindings.get(projectId);
|
|
60
345
|
if (!active() || binding?.state !== 'remote' || binding.remoteId !== remoteId)
|
|
61
346
|
return;
|
|
@@ -65,6 +350,10 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
65
350
|
this.projects.set(projectId, state);
|
|
66
351
|
}
|
|
67
352
|
try {
|
|
353
|
+
if (state.resume?.size)
|
|
354
|
+
await this.resumeOwed(projectId, state);
|
|
355
|
+
if (!active())
|
|
356
|
+
return;
|
|
68
357
|
if (state.step !== 'done') {
|
|
69
358
|
await this.move(projectId, state, active);
|
|
70
359
|
return;
|
|
@@ -72,24 +361,51 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
72
361
|
await this.checkSync(projectId, state, active);
|
|
73
362
|
if (!state.hasGit || !active())
|
|
74
363
|
return;
|
|
364
|
+
const owner = this.gitOwner.get(projectId);
|
|
365
|
+
const receiver = owner === 'vm' ? 'home' : 'vm';
|
|
366
|
+
const generation = this.gitOwner.generation(projectId);
|
|
367
|
+
if (state.headsGeneration !== generation) {
|
|
368
|
+
state.heads = undefined;
|
|
369
|
+
state.headsGeneration = generation;
|
|
370
|
+
}
|
|
75
371
|
const id = (0, file_sync_service_1.projectFolderId)(projectId, 'git');
|
|
76
|
-
const config =
|
|
372
|
+
const config = receiver === 'home'
|
|
373
|
+
? await this.files.folderConfiguration(id)
|
|
374
|
+
: await this.host.syncFolderConfiguration(remoteId, id);
|
|
77
375
|
if (!active() || config.type !== 'receiveonly' || config.paused)
|
|
78
376
|
return;
|
|
79
|
-
const status =
|
|
377
|
+
const status = receiver === 'home'
|
|
378
|
+
? await this.files.status(id)
|
|
379
|
+
: await this.host.syncStatus(remoteId, id);
|
|
80
380
|
if (!active() || status.state !== 'idle' || status.needTotalItems !== 0)
|
|
81
381
|
return;
|
|
82
382
|
state.warning = null;
|
|
83
383
|
if (status.receiveOnlyChangedFiles > 0) {
|
|
84
|
-
|
|
384
|
+
if (receiver === 'home')
|
|
385
|
+
await this.files.revertLocalChanges(id);
|
|
386
|
+
else
|
|
387
|
+
await this.host.syncRevert(remoteId, id);
|
|
85
388
|
return;
|
|
86
389
|
}
|
|
87
390
|
const root = await this.files.folderPath(projectId);
|
|
88
391
|
const head = await this.git.mirroredHead(projectId, root);
|
|
89
|
-
if (!active() || head === null || head === state.
|
|
392
|
+
if (!active() || head === null || head === state.heads?.[receiver])
|
|
90
393
|
return;
|
|
91
|
-
|
|
92
|
-
|
|
394
|
+
if (receiver === 'home') {
|
|
395
|
+
await this.git.refreshIndexFromHead(projectId, root);
|
|
396
|
+
(state.heads ??= {}).home = head;
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
const result = await this.host.refreshGitIndex(remoteId, projectId, state.heads?.vm ?? null);
|
|
400
|
+
if (!active())
|
|
401
|
+
return;
|
|
402
|
+
if (result.warning) {
|
|
403
|
+
state.warning = result.warning;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (result.refreshed && result.head !== null)
|
|
407
|
+
(state.heads ??= {}).vm = result.head;
|
|
408
|
+
}
|
|
93
409
|
}
|
|
94
410
|
catch (error) {
|
|
95
411
|
state.warning =
|
|
@@ -111,54 +427,172 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
111
427
|
const code = configurations.find((folder) => folder.kind === 'code');
|
|
112
428
|
const homeId = this.files.device().deviceId;
|
|
113
429
|
const peers = code?.config.devices.filter((device) => device.deviceID !== homeId);
|
|
114
|
-
if (peers?.length !== 1)
|
|
430
|
+
if (!code || peers?.length !== 1)
|
|
115
431
|
throw new Error('Expected one VM device in the code folder');
|
|
116
432
|
const peerId = peers[0].deviceID;
|
|
117
|
-
const [connected, statuses] = await Promise.all([
|
|
433
|
+
const [connected, statuses, vmStatus] = await Promise.all([
|
|
118
434
|
this.files.isConnected(peerId),
|
|
119
435
|
Promise.all(configurations.map(async (folder) => ({
|
|
120
436
|
...folder,
|
|
121
437
|
status: await this.files.status(folder.id, folder.kind === 'code' ? peerId : undefined),
|
|
122
438
|
}))),
|
|
439
|
+
this.host.syncStatus(state.remoteId, code.id).catch((error) => {
|
|
440
|
+
logger.warn({ error, projectId }, 'VM file sync status could not be read');
|
|
441
|
+
return null;
|
|
442
|
+
}),
|
|
123
443
|
]);
|
|
124
444
|
if (!active())
|
|
125
445
|
return;
|
|
126
446
|
const observations = new Map();
|
|
127
447
|
const warnings = [];
|
|
128
|
-
const observe = (key, count, limit, message) => {
|
|
448
|
+
const observe = (key, count, limit, message, problem, bytes) => {
|
|
129
449
|
if (count <= 0)
|
|
130
|
-
return;
|
|
450
|
+
return 0;
|
|
131
451
|
const previous = state.observations?.get(key);
|
|
132
|
-
const since = !previous ||
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
452
|
+
const since = !previous ||
|
|
453
|
+
count < previous.count ||
|
|
454
|
+
(bytes !== undefined && previous.bytes !== undefined && bytes < previous.bytes)
|
|
455
|
+
? now
|
|
456
|
+
: previous.since;
|
|
457
|
+
observations.set(key, { since, count, bytes });
|
|
458
|
+
if (now - since < limit)
|
|
459
|
+
return 0;
|
|
460
|
+
warnings.push({ message: message(new Date(since).toTimeString().slice(0, 5)), problem });
|
|
461
|
+
return count;
|
|
136
462
|
};
|
|
137
|
-
observe('connection', connected ? 0 : 1, PROBLEM_GRACE_MS, (since) => `No file sync connection to the VM since ${since}; changes do not move between this PC and the VM
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
463
|
+
observe('connection', connected ? 0 : 1, PROBLEM_GRACE_MS, (since) => `No file sync connection to the VM since ${since}; changes do not move between this PC and the VM.`, 'connection');
|
|
464
|
+
const homeCode = statuses.find((folder) => folder.kind === 'code');
|
|
465
|
+
const vmCount = vmStatus
|
|
466
|
+
? (vmStatus.errors ?? 0)
|
|
467
|
+
: (state.observations?.get('vm-failed')?.count ?? 0);
|
|
468
|
+
const vmFailed = observe('vm-failed', vmCount, PROBLEM_GRACE_MS, (since) => `${vmCount} files on the VM fail to sync since ${since}.`, 'failed-files');
|
|
469
|
+
const homeCount = homeCode.status.errors ?? 0;
|
|
470
|
+
const homeFailed = observe('home-failed', homeCount, PROBLEM_GRACE_MS, (since) => `${homeCount} files on this PC fail to sync since ${since}.`, 'failed-files');
|
|
471
|
+
for (const { id, kind, config, status } of statuses) {
|
|
472
|
+
const error = folderError(config, status, kind !== 'code');
|
|
473
|
+
observe(`error:${id}`, error ? 1 : 0, PROBLEM_GRACE_MS, (since) => `File sync at home has an error since ${since}: ${error}.`, !config.paused && !status.error?.trim() && (status.errors ?? 0) > 0
|
|
474
|
+
? 'failed-files'
|
|
475
|
+
: 'error');
|
|
141
476
|
}
|
|
142
477
|
const stalled = `no progress for ${STALLED_MS / 60_000} minutes`;
|
|
143
478
|
for (const { id, status } of statuses) {
|
|
144
|
-
observe(`home:${id}`, status.needTotalItems, STALLED_MS, (since) => `This PC has not received ${status.needTotalItems} pending file sync items since ${since} (${stalled})
|
|
479
|
+
observe(`home:${id}`, status.needTotalItems, STALLED_MS, (since) => `This PC has not received ${status.needTotalItems} pending file sync items since ${since} (${stalled}).`, 'stalled', status.needBytes);
|
|
145
480
|
const { peer } = status;
|
|
146
481
|
if (peer) {
|
|
147
|
-
observe(`peer:${id}`, peer.needItems, STALLED_MS, (since) => `The VM has not received ${peer.needItems} pending file sync items from this PC since ${since} (${stalled})
|
|
482
|
+
observe(`peer:${id}`, peer.needItems, STALLED_MS, (since) => `The VM has not received ${peer.needItems} pending file sync items from this PC since ${since} (${stalled}).`, 'stalled', peer.needBytes);
|
|
148
483
|
}
|
|
149
484
|
}
|
|
150
485
|
state.observations = observations;
|
|
151
|
-
state.checkWarning = warnings[0] ?? null;
|
|
486
|
+
state.checkWarning = warnings[0]?.message ?? null;
|
|
487
|
+
state.checkProblem = warnings[0]?.problem;
|
|
488
|
+
state.failed = homeFailed || vmFailed ? { home: homeFailed, vm: vmFailed } : undefined;
|
|
489
|
+
if (homeCount === 0 && vmCount === 0)
|
|
490
|
+
state.autoFixAttempt = undefined;
|
|
491
|
+
if (state.failed && active())
|
|
492
|
+
await this.fixAutomatically(projectId, state, { home: homeCount, vm: vmCount }, now, active);
|
|
152
493
|
}
|
|
153
494
|
catch (error) {
|
|
154
495
|
if (!active())
|
|
155
496
|
return;
|
|
497
|
+
state.checkProblem = 'error';
|
|
156
498
|
state.checkWarning = 'DevChain could not read the file sync status. It will try again.';
|
|
157
499
|
logger.warn({ error, projectId }, 'Remote file sync check failed');
|
|
158
500
|
}
|
|
159
501
|
}
|
|
502
|
+
async fixAutomatically(projectId, state, counts, now, active) {
|
|
503
|
+
if (!this.autoFix.get(projectId).enabled)
|
|
504
|
+
return;
|
|
505
|
+
const previous = state.autoFixAttempt;
|
|
506
|
+
if (previous &&
|
|
507
|
+
previous.home === counts.home &&
|
|
508
|
+
previous.vm === counts.vm &&
|
|
509
|
+
now - previous.at < STALLED_MS)
|
|
510
|
+
return;
|
|
511
|
+
state.autoFixAttempt = { ...counts, at: now };
|
|
512
|
+
try {
|
|
513
|
+
const revision = this.files.getIgnoresRevision(projectId);
|
|
514
|
+
const failed = await this.failures.failed(projectId);
|
|
515
|
+
if (!active() || !this.autoFix.get(projectId).enabled)
|
|
516
|
+
return;
|
|
517
|
+
await this.repairAutomatically(projectId, state, failed, now, active);
|
|
518
|
+
if (!active() || !this.autoFix.get(projectId).enabled)
|
|
519
|
+
return;
|
|
520
|
+
const groups = failed.groups.filter((group) => (0, sync_path_inspection_dto_1.isSelectableExclusion)(group) &&
|
|
521
|
+
group.selected &&
|
|
522
|
+
group.reasonKind === 'foreignOwner' &&
|
|
523
|
+
!group.home?.tracked &&
|
|
524
|
+
!group.vm?.tracked &&
|
|
525
|
+
group.patternChecksPassed === true);
|
|
526
|
+
const current = this.files.getIgnores(projectId);
|
|
527
|
+
const additions = groups
|
|
528
|
+
.flatMap((group) => group.patterns)
|
|
529
|
+
.filter((pattern) => !current.includes(pattern));
|
|
530
|
+
const list = [...new Set([...current, ...additions])];
|
|
531
|
+
if (additions.length === 0 || !file_sync_dto_1.IgnorePatternsSchema.safeParse(list).success)
|
|
532
|
+
return;
|
|
533
|
+
if (await this.hasOpenHold(projectId))
|
|
534
|
+
return;
|
|
535
|
+
if (!active())
|
|
536
|
+
return;
|
|
537
|
+
await this.saveIgnores(projectId, list, active, revision);
|
|
538
|
+
for (const side of ['home', 'vm']) {
|
|
539
|
+
const patterns = [
|
|
540
|
+
...new Set(groups
|
|
541
|
+
.filter((group) => group.side === side)
|
|
542
|
+
.flatMap((group) => group.patterns)
|
|
543
|
+
.filter((pattern) => additions.includes(pattern))),
|
|
544
|
+
];
|
|
545
|
+
if (patterns.length)
|
|
546
|
+
this.autoFix.record(projectId, {
|
|
547
|
+
at: new Date(now).toISOString(),
|
|
548
|
+
kind: 'exclude',
|
|
549
|
+
side,
|
|
550
|
+
patterns,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
logger.warn({ error, projectId }, 'Automatic file sync exclusions will retry');
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
async repairAutomatically(projectId, state, failed, now, active) {
|
|
559
|
+
const attempts = (state.repairAttempts ??= new Map());
|
|
560
|
+
const paths = (failed.vm?.entries ?? [])
|
|
561
|
+
.filter((entry) => entry.git?.state === 'repo' &&
|
|
562
|
+
entry.git.tracked === true &&
|
|
563
|
+
failed.groups.some((group) => group.chown && (0, sync_path_inspection_dto_1.within)(entry.path, group.path)) &&
|
|
564
|
+
now - (attempts.get(entry.path) ?? -Infinity) >= STALLED_MS)
|
|
565
|
+
.map((entry) => entry.path);
|
|
566
|
+
if (!paths.length || (await this.hasOpenHold(projectId)) || !active())
|
|
567
|
+
return;
|
|
568
|
+
const { rootPath } = await this.storage.getProject(projectId);
|
|
569
|
+
try {
|
|
570
|
+
if (!active() || !this.autoFix.get(projectId).enabled)
|
|
571
|
+
return;
|
|
572
|
+
for (const path of paths)
|
|
573
|
+
attempts.set(path, now);
|
|
574
|
+
const result = await this.host.syncChown(state.remoteId, {
|
|
575
|
+
root: rootPath,
|
|
576
|
+
items: paths.map((path) => ({ path, mode: 'automatic' })),
|
|
577
|
+
});
|
|
578
|
+
this.recordRepairs(projectId, result, now);
|
|
579
|
+
const repaired = (state.repairedAt ??= new Map());
|
|
580
|
+
for (const item of result.items.filter((item) => item.paths.length > 0)) {
|
|
581
|
+
const previous = repaired.get(item.path);
|
|
582
|
+
if (previous !== undefined && now - previous < REPEAT_REPAIR_MS)
|
|
583
|
+
(state.ownershipNotes ??= new Map()).set(item.path, now);
|
|
584
|
+
repaired.set(item.path, now);
|
|
585
|
+
}
|
|
586
|
+
if (result.items.some((item) => item.paths.length))
|
|
587
|
+
await this.retryPullErrors(projectId, 'vm', state.remoteId, active);
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
logger.warn({ error, projectId }, 'Automatic VM ownership repair will retry');
|
|
591
|
+
}
|
|
592
|
+
}
|
|
160
593
|
async move(projectId, state, active) {
|
|
161
594
|
const { remoteId } = state;
|
|
595
|
+
const owner = this.gitOwner.get(projectId);
|
|
162
596
|
const codeId = (0, file_sync_service_1.projectFolderId)(projectId, 'code');
|
|
163
597
|
const gitId = (0, file_sync_service_1.projectFolderId)(projectId, 'git');
|
|
164
598
|
if (state.step === 'create') {
|
|
@@ -173,22 +607,30 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
173
607
|
return;
|
|
174
608
|
await this.files.addPeer(host);
|
|
175
609
|
await this.host.syncPeer(remoteId, home);
|
|
176
|
-
|
|
610
|
+
const ensureVm = () => this.host.syncFolders(remoteId, {
|
|
177
611
|
projectId,
|
|
178
612
|
kind: 'git',
|
|
179
|
-
type: 'sendonly',
|
|
613
|
+
type: owner === 'vm' ? 'sendonly' : 'receiveonly',
|
|
180
614
|
peerDeviceId: home.deviceId,
|
|
181
615
|
ignores: (0, file_sync_dto_1.gitIgnores)(true),
|
|
182
616
|
paused: true,
|
|
183
617
|
});
|
|
184
|
-
|
|
618
|
+
const ensureHome = () => this.files.ensureFolder({
|
|
185
619
|
projectId,
|
|
186
620
|
kind: 'git',
|
|
187
|
-
type: 'receiveonly',
|
|
621
|
+
type: owner === 'home' ? 'sendonly' : 'receiveonly',
|
|
188
622
|
peerDeviceId: host.deviceId,
|
|
189
623
|
ignores: (0, file_sync_dto_1.gitIgnores)(true),
|
|
190
624
|
paused: true,
|
|
191
625
|
});
|
|
626
|
+
if (owner === 'vm') {
|
|
627
|
+
await ensureHome();
|
|
628
|
+
await ensureVm();
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
await ensureVm();
|
|
632
|
+
await ensureHome();
|
|
633
|
+
}
|
|
192
634
|
await this.host.syncFolderType(remoteId, gitId, { paused: false });
|
|
193
635
|
await this.files.updateFolder(gitId, { paused: false });
|
|
194
636
|
await Promise.all([this.host.syncScan(remoteId, gitId), this.files.rescan(gitId)]);
|
|
@@ -198,17 +640,27 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
198
640
|
if (!active())
|
|
199
641
|
return;
|
|
200
642
|
if (state.step === 'settle') {
|
|
201
|
-
const config =
|
|
643
|
+
const config = owner === 'vm'
|
|
644
|
+
? await this.files.folderConfiguration(gitId)
|
|
645
|
+
: await this.host.syncFolderConfiguration(remoteId, gitId);
|
|
202
646
|
if (config.type !== 'receiveonly' || config.paused)
|
|
203
647
|
throw new Error('Git share is not receiving');
|
|
204
|
-
const [sender, receiver] = await Promise.all(
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
648
|
+
const [sender, receiver] = await Promise.all(owner === 'vm'
|
|
649
|
+
? [
|
|
650
|
+
this.host.syncStatus(remoteId, gitId, this.files.device().deviceId),
|
|
651
|
+
this.files.status(gitId),
|
|
652
|
+
]
|
|
653
|
+
: [
|
|
654
|
+
this.files.status(gitId, (await this.host.syncDevice(remoteId)).deviceId),
|
|
655
|
+
this.host.syncStatus(remoteId, gitId),
|
|
656
|
+
]);
|
|
208
657
|
if (!active())
|
|
209
658
|
return;
|
|
210
659
|
if (receiver.state === 'idle' && receiver.receiveOnlyChangedFiles > 0) {
|
|
211
|
-
|
|
660
|
+
if (owner === 'vm')
|
|
661
|
+
await this.files.revertLocalChanges(gitId);
|
|
662
|
+
else
|
|
663
|
+
await this.host.syncRevert(remoteId, gitId);
|
|
212
664
|
return;
|
|
213
665
|
}
|
|
214
666
|
if (!(0, file_sync_service_1.evaluateCompletion)(sender, receiver).done) {
|
|
@@ -229,15 +681,21 @@ let RemoteFileSyncService = class RemoteFileSyncService {
|
|
|
229
681
|
if (!active())
|
|
230
682
|
return;
|
|
231
683
|
if (state.step === 'directions') {
|
|
232
|
-
await this.host.syncFolderType(remoteId, codeId, { type: 'sendreceive' });
|
|
233
|
-
await this.files.updateFolder(codeId, { type: 'sendreceive' });
|
|
684
|
+
await this.host.syncFolderType(remoteId, codeId, { type: 'sendreceive', paused: false });
|
|
685
|
+
await this.files.updateFolder(codeId, { type: 'sendreceive', paused: false });
|
|
234
686
|
state.step = 'guard';
|
|
235
687
|
}
|
|
236
688
|
if (!active())
|
|
237
689
|
return;
|
|
238
690
|
if (state.step === 'guard') {
|
|
239
691
|
if (state.hasGit) {
|
|
240
|
-
state.guardWarning =
|
|
692
|
+
state.guardWarning =
|
|
693
|
+
owner === 'vm'
|
|
694
|
+
? await this.guard.install(projectId, remoteId)
|
|
695
|
+
: (await this.host.installGitGuard(remoteId, projectId, {
|
|
696
|
+
homeName: (0, node_os_1.hostname)(),
|
|
697
|
+
reason: 'pc-git',
|
|
698
|
+
})).warning;
|
|
241
699
|
}
|
|
242
700
|
state.step = 'done';
|
|
243
701
|
state.warning = null;
|
|
@@ -248,19 +706,22 @@ exports.RemoteFileSyncService = RemoteFileSyncService;
|
|
|
248
706
|
exports.RemoteFileSyncService = RemoteFileSyncService = __decorate([
|
|
249
707
|
(0, common_1.Injectable)(),
|
|
250
708
|
__param(3, (0, common_1.Inject)(remote_health_port_1.REMOTE_HEALTH_PORT)),
|
|
709
|
+
__param(7, (0, common_1.Inject)(storage_interface_1.STORAGE_SERVICE)),
|
|
251
710
|
__metadata("design:paramtypes", [file_sync_service_1.FileSyncService,
|
|
252
711
|
remote_host_client_1.RemoteHostClient,
|
|
253
712
|
remote_bindings_service_1.RemoteBindingsService, Object, file_sync_managed_exclusions_store_1.FileSyncManagedExclusionsStore,
|
|
254
713
|
git_service_1.GitService,
|
|
255
|
-
home_git_guard_service_1.HomeGitGuardService
|
|
714
|
+
home_git_guard_service_1.HomeGitGuardService, Object, file_sync_auto_fix_store_1.FileSyncAutoFixStore,
|
|
715
|
+
file_sync_failures_service_1.FileSyncFailuresService,
|
|
716
|
+
git_owner_store_1.GitOwnerStore])
|
|
256
717
|
], RemoteFileSyncService);
|
|
257
|
-
function folderError(config, status) {
|
|
718
|
+
function folderError(config, status, includeFailedFiles = true) {
|
|
258
719
|
if (config.paused)
|
|
259
720
|
return 'folder is paused';
|
|
260
721
|
const message = status.error?.trim();
|
|
261
722
|
if (message)
|
|
262
723
|
return message;
|
|
263
|
-
if ((status.errors ?? 0) > 0)
|
|
724
|
+
if (includeFailedFiles && (status.errors ?? 0) > 0)
|
|
264
725
|
return `${status.errors} files failed`;
|
|
265
726
|
if (status.state === 'error')
|
|
266
727
|
return 'folder is in an error state';
|