xtep-workspace 1.10.10
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/CHANGELOG.md +525 -0
- package/README.md +188 -0
- package/config/component-versions.json +16 -0
- package/config/scenarioCapabilities.json +29 -0
- package/config/version-notes.yaml +244 -0
- package/dist/adapters/OutputAdapter.d.ts +79 -0
- package/dist/adapters/OutputAdapter.d.ts.map +1 -0
- package/dist/adapters/OutputAdapter.js +124 -0
- package/dist/adapters/OutputAdapter.js.map +1 -0
- package/dist/cli/check-node-version.d.ts +3 -0
- package/dist/cli/check-node-version.d.ts.map +1 -0
- package/dist/cli/check-node-version.js +153 -0
- package/dist/cli/check-node-version.js.map +1 -0
- package/dist/cli/plugins.d.ts +41 -0
- package/dist/cli/plugins.d.ts.map +1 -0
- package/dist/cli/plugins.js +742 -0
- package/dist/cli/plugins.js.map +1 -0
- package/dist/cli/rebuild.d.ts +63 -0
- package/dist/cli/rebuild.d.ts.map +1 -0
- package/dist/cli/rebuild.js +989 -0
- package/dist/cli/rebuild.js.map +1 -0
- package/dist/cli/repair.d.ts +7 -0
- package/dist/cli/repair.d.ts.map +1 -0
- package/dist/cli/repair.js +925 -0
- package/dist/cli/repair.js.map +1 -0
- package/dist/cli/setup.d.ts +7 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +452 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/update.d.ts +10 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +426 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/webui.d.ts +6 -0
- package/dist/cli/webui.d.ts.map +1 -0
- package/dist/cli/webui.js +210 -0
- package/dist/cli/webui.js.map +1 -0
- package/dist/http/index.d.ts +3 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +15 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/middleware/errorHandler.d.ts +16 -0
- package/dist/http/middleware/errorHandler.d.ts.map +1 -0
- package/dist/http/middleware/errorHandler.js +79 -0
- package/dist/http/middleware/errorHandler.js.map +1 -0
- package/dist/http/routes/admin.d.ts +3 -0
- package/dist/http/routes/admin.d.ts.map +1 -0
- package/dist/http/routes/admin.js +730 -0
- package/dist/http/routes/admin.js.map +1 -0
- package/dist/http/routes/backup.d.ts +3 -0
- package/dist/http/routes/backup.d.ts.map +1 -0
- package/dist/http/routes/backup.js +172 -0
- package/dist/http/routes/backup.js.map +1 -0
- package/dist/http/routes/config.d.ts +3 -0
- package/dist/http/routes/config.d.ts.map +1 -0
- package/dist/http/routes/config.js +157 -0
- package/dist/http/routes/config.js.map +1 -0
- package/dist/http/routes/context.d.ts +3 -0
- package/dist/http/routes/context.d.ts.map +1 -0
- package/dist/http/routes/context.js +82 -0
- package/dist/http/routes/context.js.map +1 -0
- package/dist/http/routes/log.d.ts +3 -0
- package/dist/http/routes/log.d.ts.map +1 -0
- package/dist/http/routes/log.js +105 -0
- package/dist/http/routes/log.js.map +1 -0
- package/dist/http/routes/memo.d.ts +6 -0
- package/dist/http/routes/memo.d.ts.map +1 -0
- package/dist/http/routes/memo.js +29 -0
- package/dist/http/routes/memo.js.map +1 -0
- package/dist/http/routes/node.d.ts +3 -0
- package/dist/http/routes/node.d.ts.map +1 -0
- package/dist/http/routes/node.js +251 -0
- package/dist/http/routes/node.js.map +1 -0
- package/dist/http/routes/state.d.ts +3 -0
- package/dist/http/routes/state.d.ts.map +1 -0
- package/dist/http/routes/state.js +48 -0
- package/dist/http/routes/state.js.map +1 -0
- package/dist/http/routes/workspace.d.ts +3 -0
- package/dist/http/routes/workspace.d.ts.map +1 -0
- package/dist/http/routes/workspace.js +249 -0
- package/dist/http/routes/workspace.js.map +1 -0
- package/dist/http/server.d.ts +10 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +284 -0
- package/dist/http/server.js.map +1 -0
- package/dist/http/services.d.ts +93 -0
- package/dist/http/services.d.ts.map +1 -0
- package/dist/http/services.js +297 -0
- package/dist/http/services.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1073 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/guidanceContent.d.ts +18 -0
- package/dist/prompts/guidanceContent.d.ts.map +1 -0
- package/dist/prompts/guidanceContent.js +814 -0
- package/dist/prompts/guidanceContent.js.map +1 -0
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +4 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/instructions.d.ts +56 -0
- package/dist/prompts/instructions.d.ts.map +1 -0
- package/dist/prompts/instructions.js +1343 -0
- package/dist/prompts/instructions.js.map +1 -0
- package/dist/services/BackupService.d.ts +104 -0
- package/dist/services/BackupService.d.ts.map +1 -0
- package/dist/services/BackupService.js +549 -0
- package/dist/services/BackupService.js.map +1 -0
- package/dist/services/CapabilityService.d.ts +38 -0
- package/dist/services/CapabilityService.d.ts.map +1 -0
- package/dist/services/CapabilityService.js +256 -0
- package/dist/services/CapabilityService.js.map +1 -0
- package/dist/services/ConfigService.d.ts +35 -0
- package/dist/services/ConfigService.d.ts.map +1 -0
- package/dist/services/ConfigService.js +105 -0
- package/dist/services/ConfigService.js.map +1 -0
- package/dist/services/ContextService.d.ts +65 -0
- package/dist/services/ContextService.d.ts.map +1 -0
- package/dist/services/ContextService.js +503 -0
- package/dist/services/ContextService.js.map +1 -0
- package/dist/services/DetectionService.d.ts +76 -0
- package/dist/services/DetectionService.d.ts.map +1 -0
- package/dist/services/DetectionService.js +262 -0
- package/dist/services/DetectionService.js.map +1 -0
- package/dist/services/DispatchService.d.ts +267 -0
- package/dist/services/DispatchService.d.ts.map +1 -0
- package/dist/services/DispatchService.js +1357 -0
- package/dist/services/DispatchService.js.map +1 -0
- package/dist/services/EventService.d.ts +81 -0
- package/dist/services/EventService.d.ts.map +1 -0
- package/dist/services/EventService.js +187 -0
- package/dist/services/EventService.js.map +1 -0
- package/dist/services/GuidanceService.d.ts +64 -0
- package/dist/services/GuidanceService.d.ts.map +1 -0
- package/dist/services/GuidanceService.js +259 -0
- package/dist/services/GuidanceService.js.map +1 -0
- package/dist/services/HealthService.d.ts +43 -0
- package/dist/services/HealthService.d.ts.map +1 -0
- package/dist/services/HealthService.js +276 -0
- package/dist/services/HealthService.js.map +1 -0
- package/dist/services/InstallationService.d.ts +62 -0
- package/dist/services/InstallationService.d.ts.map +1 -0
- package/dist/services/InstallationService.js +204 -0
- package/dist/services/InstallationService.js.map +1 -0
- package/dist/services/LogService.d.ts +35 -0
- package/dist/services/LogService.d.ts.map +1 -0
- package/dist/services/LogService.js +189 -0
- package/dist/services/LogService.js.map +1 -0
- package/dist/services/MemoService.d.ts +39 -0
- package/dist/services/MemoService.d.ts.map +1 -0
- package/dist/services/MemoService.js +288 -0
- package/dist/services/MemoService.js.map +1 -0
- package/dist/services/NodeService.d.ts +90 -0
- package/dist/services/NodeService.d.ts.map +1 -0
- package/dist/services/NodeService.js +958 -0
- package/dist/services/NodeService.js.map +1 -0
- package/dist/services/OpenSpecParser.d.ts +43 -0
- package/dist/services/OpenSpecParser.d.ts.map +1 -0
- package/dist/services/OpenSpecParser.js +191 -0
- package/dist/services/OpenSpecParser.js.map +1 -0
- package/dist/services/ReferenceService.d.ts +35 -0
- package/dist/services/ReferenceService.d.ts.map +1 -0
- package/dist/services/ReferenceService.js +195 -0
- package/dist/services/ReferenceService.js.map +1 -0
- package/dist/services/RepairService.d.ts +36 -0
- package/dist/services/RepairService.d.ts.map +1 -0
- package/dist/services/RepairService.js +429 -0
- package/dist/services/RepairService.js.map +1 -0
- package/dist/services/SearchService.d.ts +34 -0
- package/dist/services/SearchService.d.ts.map +1 -0
- package/dist/services/SearchService.js +293 -0
- package/dist/services/SearchService.js.map +1 -0
- package/dist/services/SessionService.d.ts +136 -0
- package/dist/services/SessionService.d.ts.map +1 -0
- package/dist/services/SessionService.js +297 -0
- package/dist/services/SessionService.js.map +1 -0
- package/dist/services/StateService.d.ts +97 -0
- package/dist/services/StateService.d.ts.map +1 -0
- package/dist/services/StateService.js +846 -0
- package/dist/services/StateService.js.map +1 -0
- package/dist/services/TutorialService.d.ts +114 -0
- package/dist/services/TutorialService.d.ts.map +1 -0
- package/dist/services/TutorialService.js +1262 -0
- package/dist/services/TutorialService.js.map +1 -0
- package/dist/services/WorkspaceService.d.ts +273 -0
- package/dist/services/WorkspaceService.d.ts.map +1 -0
- package/dist/services/WorkspaceService.js +1764 -0
- package/dist/services/WorkspaceService.js.map +1 -0
- package/dist/services/index.d.ts +15 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +14 -0
- package/dist/services/index.js.map +1 -0
- package/dist/storage/FileSystemAdapter.d.ts +223 -0
- package/dist/storage/FileSystemAdapter.d.ts.map +1 -0
- package/dist/storage/FileSystemAdapter.js +384 -0
- package/dist/storage/FileSystemAdapter.js.map +1 -0
- package/dist/storage/JsonStorage.d.ts +158 -0
- package/dist/storage/JsonStorage.d.ts.map +1 -0
- package/dist/storage/JsonStorage.js +613 -0
- package/dist/storage/JsonStorage.js.map +1 -0
- package/dist/storage/MarkdownStorage.d.ts +178 -0
- package/dist/storage/MarkdownStorage.d.ts.map +1 -0
- package/dist/storage/MarkdownStorage.js +918 -0
- package/dist/storage/MarkdownStorage.js.map +1 -0
- package/dist/storage/SessionBindingStorage.d.ts +69 -0
- package/dist/storage/SessionBindingStorage.d.ts.map +1 -0
- package/dist/storage/SessionBindingStorage.js +131 -0
- package/dist/storage/SessionBindingStorage.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/tools/capability.d.ts +18 -0
- package/dist/tools/capability.d.ts.map +1 -0
- package/dist/tools/capability.js +73 -0
- package/dist/tools/capability.js.map +1 -0
- package/dist/tools/config.d.ts +14 -0
- package/dist/tools/config.d.ts.map +1 -0
- package/dist/tools/config.js +61 -0
- package/dist/tools/config.js.map +1 -0
- package/dist/tools/context.d.ts +22 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +139 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/dispatch.d.ts +41 -0
- package/dist/tools/dispatch.d.ts.map +1 -0
- package/dist/tools/dispatch.js +380 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/help.d.ts +44 -0
- package/dist/tools/help.d.ts.map +1 -0
- package/dist/tools/help.js +227 -0
- package/dist/tools/help.js.map +1 -0
- package/dist/tools/import.d.ts +17 -0
- package/dist/tools/import.d.ts.map +1 -0
- package/dist/tools/import.js +96 -0
- package/dist/tools/import.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +13 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/log.d.ts +21 -0
- package/dist/tools/log.d.ts.map +1 -0
- package/dist/tools/log.js +93 -0
- package/dist/tools/log.js.map +1 -0
- package/dist/tools/memo.d.ts +26 -0
- package/dist/tools/memo.d.ts.map +1 -0
- package/dist/tools/memo.js +188 -0
- package/dist/tools/memo.js.map +1 -0
- package/dist/tools/node.d.ts +34 -0
- package/dist/tools/node.d.ts.map +1 -0
- package/dist/tools/node.js +328 -0
- package/dist/tools/node.js.map +1 -0
- package/dist/tools/search.d.ts +14 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +95 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/session.d.ts +22 -0
- package/dist/tools/session.d.ts.map +1 -0
- package/dist/tools/session.js +127 -0
- package/dist/tools/session.js.map +1 -0
- package/dist/tools/state.d.ts +10 -0
- package/dist/tools/state.d.ts.map +1 -0
- package/dist/tools/state.js +79 -0
- package/dist/tools/state.js.map +1 -0
- package/dist/tools/workspace.d.ts +38 -0
- package/dist/tools/workspace.d.ts.map +1 -0
- package/dist/tools/workspace.js +240 -0
- package/dist/tools/workspace.js.map +1 -0
- package/dist/types/capability.d.ts +36 -0
- package/dist/types/capability.d.ts.map +1 -0
- package/dist/types/capability.js +3 -0
- package/dist/types/capability.js.map +1 -0
- package/dist/types/confirmation.d.ts +35 -0
- package/dist/types/confirmation.d.ts.map +1 -0
- package/dist/types/confirmation.js +3 -0
- package/dist/types/confirmation.js.map +1 -0
- package/dist/types/context.d.ts +174 -0
- package/dist/types/context.d.ts.map +1 -0
- package/dist/types/context.js +3 -0
- package/dist/types/context.js.map +1 -0
- package/dist/types/errors.d.ts +81 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +154 -0
- package/dist/types/errors.js.map +1 -0
- package/dist/types/guidance.d.ts +162 -0
- package/dist/types/guidance.d.ts.map +1 -0
- package/dist/types/guidance.js +4 -0
- package/dist/types/guidance.js.map +1 -0
- package/dist/types/health.d.ts +61 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +3 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/memo.d.ts +132 -0
- package/dist/types/memo.d.ts.map +1 -0
- package/dist/types/memo.js +3 -0
- package/dist/types/memo.js.map +1 -0
- package/dist/types/node.d.ts +316 -0
- package/dist/types/node.d.ts.map +1 -0
- package/dist/types/node.js +3 -0
- package/dist/types/node.js.map +1 -0
- package/dist/types/repair.d.ts +62 -0
- package/dist/types/repair.d.ts.map +1 -0
- package/dist/types/repair.js +4 -0
- package/dist/types/repair.js.map +1 -0
- package/dist/types/search.d.ts +58 -0
- package/dist/types/search.d.ts.map +1 -0
- package/dist/types/search.js +3 -0
- package/dist/types/search.js.map +1 -0
- package/dist/types/settings.d.ts +109 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/settings.js +30 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/types/workspace.d.ts +357 -0
- package/dist/types/workspace.d.ts.map +1 -0
- package/dist/types/workspace.js +3 -0
- package/dist/types/workspace.js.map +1 -0
- package/dist/utils/contentValidation.d.ts +47 -0
- package/dist/utils/contentValidation.d.ts.map +1 -0
- package/dist/utils/contentValidation.js +93 -0
- package/dist/utils/contentValidation.js.map +1 -0
- package/dist/utils/devLog.d.ts +43 -0
- package/dist/utils/devLog.d.ts.map +1 -0
- package/dist/utils/devLog.js +94 -0
- package/dist/utils/devLog.js.map +1 -0
- package/dist/utils/errorLogger.d.ts +27 -0
- package/dist/utils/errorLogger.d.ts.map +1 -0
- package/dist/utils/errorLogger.js +105 -0
- package/dist/utils/errorLogger.js.map +1 -0
- package/dist/utils/git.d.ts +123 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +400 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/hash.d.ts +32 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +37 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/id.d.ts +54 -0
- package/dist/utils/id.d.ts.map +1 -0
- package/dist/utils/id.js +96 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +42 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +228 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/manualChangeFormatter.d.ts +8 -0
- package/dist/utils/manualChangeFormatter.d.ts.map +1 -0
- package/dist/utils/manualChangeFormatter.js +21 -0
- package/dist/utils/manualChangeFormatter.js.map +1 -0
- package/dist/utils/paramValidator.d.ts +35 -0
- package/dist/utils/paramValidator.d.ts.map +1 -0
- package/dist/utils/paramValidator.js +214 -0
- package/dist/utils/paramValidator.js.map +1 -0
- package/dist/utils/port.d.ts +7 -0
- package/dist/utils/port.d.ts.map +1 -0
- package/dist/utils/port.js +28 -0
- package/dist/utils/port.js.map +1 -0
- package/dist/utils/processManager.d.ts +53 -0
- package/dist/utils/processManager.d.ts.map +1 -0
- package/dist/utils/processManager.js +267 -0
- package/dist/utils/processManager.js.map +1 -0
- package/dist/utils/sessionLogger.d.ts +28 -0
- package/dist/utils/sessionLogger.d.ts.map +1 -0
- package/dist/utils/sessionLogger.js +142 -0
- package/dist/utils/sessionLogger.js.map +1 -0
- package/dist/utils/time.d.ts +15 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +32 -0
- package/dist/utils/time.js.map +1 -0
- package/dist/utils/validation.d.ts +23 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +88 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs//346/227/245/345/277/227/347/263/273/347/273/237.md +389 -0
- package/docs//347/224/250/346/210/267/346/211/213/345/206/214.md +1446 -0
- package/docs//347/224/250/346/210/267/346/211/213/345/206/214/344/270/216/346/212/200/346/234/257/346/214/207/345/215/227.md +873 -0
- package/package.json +89 -0
- package/plugin/README.md +141 -0
- package/plugin/agents/xtep-executor.md +114 -0
- package/plugin/agents/xtep-reviewer.md +133 -0
- package/plugin/docs/diagnostic-guide.md +128 -0
- package/plugin/hooks/hooks.json.deprecated +70 -0
- package/plugin/scripts/cursor-hook-entry.cjs +217 -0
- package/plugin/scripts/hook-entry.cjs +663 -0
- package/plugin/scripts/openspec-import.cjs +714 -0
- package/plugin/scripts/shared/binding.cjs +98 -0
- package/plugin/scripts/shared/config.cjs +65 -0
- package/plugin/scripts/shared/context.cjs +120 -0
- package/plugin/scripts/shared/index.cjs +34 -0
- package/plugin/scripts/shared/logger.cjs +196 -0
- package/plugin/scripts/shared/reminder.cjs +261 -0
- package/plugin/scripts/shared/utils.cjs +62 -0
- package/plugin/scripts/shared/workspace.cjs +322 -0
- package/plugin/skills/aligning-intent/SKILL.md +275 -0
- package/plugin/skills/analyzing-measurements/SKILL.md +223 -0
- package/plugin/skills/bootstrapping-workspace/SKILL.md +260 -0
- package/plugin/skills/designing-solutions/SKILL.md +363 -0
- package/plugin/skills/diagnosing-issues/SKILL.md +219 -0
- package/plugin/skills/discovering-context/SKILL.md +283 -0
- package/plugin/skills/dispatching-parent/SKILL.md +399 -0
- package/plugin/skills/executing-task/SKILL.md +340 -0
- package/plugin/skills/memo-create/SKILL.md +222 -0
- package/plugin/skills/planning-verification/SKILL.md +245 -0
- package/plugin/skills/preparing-dispatch/SKILL.md +299 -0
- package/plugin/skills/researching-tech/SKILL.md +223 -0
- package/plugin/skills/reviewing-quality/SKILL.md +354 -0
- package/plugin/skills/reviewing-spec/SKILL.md +333 -0
- package/plugin/skills/starting-info-flow/SKILL.md +196 -0
- package/web/README.md +5 -0
- package//351/205/215/347/275/256/346/226/271/345/274/217.md +330 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instructions.js","sourceRoot":"","sources":["../../src/prompts/instructions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { FileSystemAdapter } from "../storage/FileSystemAdapter.js";
|
|
2
|
+
import type { JsonStorage } from "../storage/JsonStorage.js";
|
|
3
|
+
import type { BackupMeta, BackupListItem, GlobalBackupTrigger, GlobalBackupItem } from "../types/health.js";
|
|
4
|
+
/**
|
|
5
|
+
* 备份服务
|
|
6
|
+
* 管理工作区级别的备份和恢复
|
|
7
|
+
*/
|
|
8
|
+
export declare class BackupService {
|
|
9
|
+
private json;
|
|
10
|
+
private fs;
|
|
11
|
+
private static readonly BACKUP_DIR;
|
|
12
|
+
private static readonly META_FILE;
|
|
13
|
+
private static readonly MAX_BACKUPS;
|
|
14
|
+
private static readonly DEBOUNCE_MS;
|
|
15
|
+
private lastAutoBackup;
|
|
16
|
+
private currentCodeVersion;
|
|
17
|
+
constructor(json: JsonStorage, fs: FileSystemAdapter);
|
|
18
|
+
/**
|
|
19
|
+
* 获取当前代码版本
|
|
20
|
+
*/
|
|
21
|
+
private getCurrentCodeVersion;
|
|
22
|
+
/**
|
|
23
|
+
* 获取备份目录路径
|
|
24
|
+
*/
|
|
25
|
+
private getBackupDir;
|
|
26
|
+
/**
|
|
27
|
+
* 获取备份元信息文件路径
|
|
28
|
+
*/
|
|
29
|
+
private getMetaPath;
|
|
30
|
+
/**
|
|
31
|
+
* 读取备份元信息
|
|
32
|
+
*/
|
|
33
|
+
private readMeta;
|
|
34
|
+
/**
|
|
35
|
+
* 写入备份元信息
|
|
36
|
+
*/
|
|
37
|
+
private writeMeta;
|
|
38
|
+
/**
|
|
39
|
+
* 生成备份文件名
|
|
40
|
+
*/
|
|
41
|
+
private generateBackupName;
|
|
42
|
+
/**
|
|
43
|
+
* 创建工作区备份
|
|
44
|
+
* @param workspaceId 工作区 ID
|
|
45
|
+
* @param trigger 触发类型
|
|
46
|
+
* @returns 备份元信息
|
|
47
|
+
*/
|
|
48
|
+
createBackup(workspaceId: string, trigger?: BackupMeta["trigger"]): Promise<BackupMeta>;
|
|
49
|
+
/**
|
|
50
|
+
* 轮转备份:删除超出数量限制的旧备份
|
|
51
|
+
*/
|
|
52
|
+
private rotateBackups;
|
|
53
|
+
/**
|
|
54
|
+
* 列出工作区备份
|
|
55
|
+
*/
|
|
56
|
+
listBackups(workspaceId: string): Promise<BackupListItem[]>;
|
|
57
|
+
/**
|
|
58
|
+
* 恢复工作区备份
|
|
59
|
+
* @param workspaceId 工作区 ID
|
|
60
|
+
* @param backupName 备份文件名
|
|
61
|
+
*/
|
|
62
|
+
restoreBackup(workspaceId: string, backupName: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* 删除指定备份
|
|
65
|
+
*/
|
|
66
|
+
deleteBackup(workspaceId: string, backupName: string): Promise<void>;
|
|
67
|
+
private static readonly GLOBAL_BACKUP_DIR;
|
|
68
|
+
private static readonly GLOBAL_BACKUP_FILES;
|
|
69
|
+
/**
|
|
70
|
+
* 获取全局备份目录路径
|
|
71
|
+
*/
|
|
72
|
+
private getGlobalBackupDir;
|
|
73
|
+
/**
|
|
74
|
+
* 生成全局备份文件名
|
|
75
|
+
*/
|
|
76
|
+
private generateGlobalBackupName;
|
|
77
|
+
/**
|
|
78
|
+
* 计算文件内容的 SHA256 校验和
|
|
79
|
+
*/
|
|
80
|
+
private calculateChecksum;
|
|
81
|
+
/**
|
|
82
|
+
* 创建全局备份
|
|
83
|
+
* @param trigger 触发类型
|
|
84
|
+
* @returns 备份信息
|
|
85
|
+
*/
|
|
86
|
+
createGlobalBackup(trigger: GlobalBackupTrigger): Promise<GlobalBackupItem>;
|
|
87
|
+
/**
|
|
88
|
+
* 列出全局备份
|
|
89
|
+
* @returns 备份列表,按时间倒序
|
|
90
|
+
*/
|
|
91
|
+
listGlobalBackups(): Promise<GlobalBackupItem[]>;
|
|
92
|
+
/**
|
|
93
|
+
* 恢复全局备份
|
|
94
|
+
* @param backupPath 备份文件完整路径
|
|
95
|
+
* @returns 恢复前自动创建的 pre_restore 备份信息
|
|
96
|
+
*/
|
|
97
|
+
restoreGlobalBackup(backupPath: string): Promise<GlobalBackupItem>;
|
|
98
|
+
/**
|
|
99
|
+
* 删除全局备份
|
|
100
|
+
* @param backupName 备份文件名
|
|
101
|
+
*/
|
|
102
|
+
deleteGlobalBackup(backupName: string): Promise<void>;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=BackupService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BackupService.d.ts","sourceRoot":"","sources":["../../src/services/BackupService.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,mBAAmB,EAEnB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAM5B;;;GAGG;AACH,qBAAa,aAAa;IAatB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,EAAE;IAbZ,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAc;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAsB;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAM;IACzC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAiB;IAGpD,OAAO,CAAC,cAAc,CAAkC;IAGxD,OAAO,CAAC,kBAAkB,CAAuB;gBAGvC,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,iBAAiB;IAG/B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAc7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAIpB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;YACW,QAAQ;IAatB;;OAEG;YACW,SAAS;IAKvB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAM1B;;;;;OAKG;IACG,YAAY,CAChB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,UAAU,CAAC,SAAS,CAAY,GACxC,OAAO,CAAC,UAAU,CAAC;IAmFtB;;OAEG;YACW,aAAa;IA4B3B;;OAEG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAmBjE;;;;OAIG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuC3E;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB1E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAa;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAIzC;IAEF;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAI1B;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAMhC;;OAEG;YACW,iBAAiB;IAgB/B;;;;OAIG;IACG,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwGjF;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA0DtD;;;;OAIG;IACG,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAgGxE;;;OAGG;IACG,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAe5D"}
|
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
// src/services/BackupService.ts
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as fs from "node:fs/promises";
|
|
4
|
+
import { exec } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { dirname, join } from "path";
|
|
8
|
+
import { createRequire } from "module";
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { createWriteStream } from "node:fs";
|
|
11
|
+
import archiver from "archiver";
|
|
12
|
+
import AdmZip from "adm-zip";
|
|
13
|
+
import { XtepError } from "../types/errors.js";
|
|
14
|
+
import { devLog } from "../utils/devLog.js";
|
|
15
|
+
const execAsync = promisify(exec);
|
|
16
|
+
/**
|
|
17
|
+
* 备份服务
|
|
18
|
+
* 管理工作区级别的备份和恢复
|
|
19
|
+
*/
|
|
20
|
+
export class BackupService {
|
|
21
|
+
json;
|
|
22
|
+
fs;
|
|
23
|
+
static BACKUP_DIR = ".backups";
|
|
24
|
+
static META_FILE = "backup-meta.json";
|
|
25
|
+
static MAX_BACKUPS = 10;
|
|
26
|
+
static DEBOUNCE_MS = 5 * 60 * 1000; // 5 分钟
|
|
27
|
+
// 防抖:记录每个工作区最后一次自动备份时间
|
|
28
|
+
lastAutoBackup = new Map();
|
|
29
|
+
// 缓存代码版本
|
|
30
|
+
currentCodeVersion = null;
|
|
31
|
+
constructor(json, fs) {
|
|
32
|
+
this.json = json;
|
|
33
|
+
this.fs = fs;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 获取当前代码版本
|
|
37
|
+
*/
|
|
38
|
+
getCurrentCodeVersion() {
|
|
39
|
+
if (this.currentCodeVersion)
|
|
40
|
+
return this.currentCodeVersion;
|
|
41
|
+
try {
|
|
42
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
43
|
+
const __dirname = dirname(__filename);
|
|
44
|
+
const require = createRequire(import.meta.url);
|
|
45
|
+
const pkg = require(join(__dirname, "..", "..", "package.json"));
|
|
46
|
+
this.currentCodeVersion = pkg.version;
|
|
47
|
+
return this.currentCodeVersion;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return "0.0.0";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 获取备份目录路径
|
|
55
|
+
*/
|
|
56
|
+
getBackupDir(projectRoot, wsDirName) {
|
|
57
|
+
return path.join(this.fs.getWorkspacePath(projectRoot, wsDirName), BackupService.BACKUP_DIR);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 获取备份元信息文件路径
|
|
61
|
+
*/
|
|
62
|
+
getMetaPath(projectRoot, wsDirName) {
|
|
63
|
+
return path.join(this.getBackupDir(projectRoot, wsDirName), BackupService.META_FILE);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 读取备份元信息
|
|
67
|
+
*/
|
|
68
|
+
async readMeta(projectRoot, wsDirName) {
|
|
69
|
+
const metaPath = this.getMetaPath(projectRoot, wsDirName);
|
|
70
|
+
try {
|
|
71
|
+
if (!(await this.fs.exists(metaPath))) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
const content = await this.fs.readFile(metaPath);
|
|
75
|
+
return JSON.parse(content);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 写入备份元信息
|
|
83
|
+
*/
|
|
84
|
+
async writeMeta(projectRoot, wsDirName, metas) {
|
|
85
|
+
const metaPath = this.getMetaPath(projectRoot, wsDirName);
|
|
86
|
+
await this.fs.writeFile(metaPath, JSON.stringify(metas, null, 2));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 生成备份文件名
|
|
90
|
+
*/
|
|
91
|
+
generateBackupName() {
|
|
92
|
+
// ISO 时间戳,替换冒号为短横线
|
|
93
|
+
const timestamp = new Date().toISOString().replace(/:/g, "-");
|
|
94
|
+
return `backup_${timestamp}.tar.gz`;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 创建工作区备份
|
|
98
|
+
* @param workspaceId 工作区 ID
|
|
99
|
+
* @param trigger 触发类型
|
|
100
|
+
* @returns 备份元信息
|
|
101
|
+
*/
|
|
102
|
+
async createBackup(workspaceId, trigger = "manual") {
|
|
103
|
+
// 获取工作区信息
|
|
104
|
+
const wsEntry = await this.json.findWorkspaceEntry(workspaceId);
|
|
105
|
+
if (!wsEntry) {
|
|
106
|
+
throw new XtepError("WORKSPACE_NOT_FOUND", `工作区 "${workspaceId}" 不存在`);
|
|
107
|
+
}
|
|
108
|
+
const { projectRoot, dirName, name: workspaceName } = wsEntry;
|
|
109
|
+
const wsDirName = dirName || workspaceId;
|
|
110
|
+
// 自动备份防抖检查
|
|
111
|
+
if (trigger === "auto") {
|
|
112
|
+
const lastTime = this.lastAutoBackup.get(workspaceId);
|
|
113
|
+
if (lastTime && Date.now() - lastTime < BackupService.DEBOUNCE_MS) {
|
|
114
|
+
throw new XtepError("INVALID_PARAMS", `工作区 "${workspaceId}" 在 5 分钟内已自动备份,跳过`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// 确保备份目录存在
|
|
118
|
+
const backupDir = this.getBackupDir(projectRoot, wsDirName);
|
|
119
|
+
await this.fs.ensureDir(backupDir);
|
|
120
|
+
// 生成备份文件名和路径
|
|
121
|
+
const backupName = this.generateBackupName();
|
|
122
|
+
const backupPath = path.join(backupDir, backupName);
|
|
123
|
+
const workspacePath = this.fs.getWorkspacePath(projectRoot, wsDirName);
|
|
124
|
+
// 创建压缩备份(排除 .backups 目录)
|
|
125
|
+
try {
|
|
126
|
+
await execAsync(`tar -czf "${backupPath}" --exclude='.backups' -C "${workspacePath}" .`);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
throw new XtepError("INVALID_PARAMS", `备份创建失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
130
|
+
}
|
|
131
|
+
// 验证备份
|
|
132
|
+
let verified = false;
|
|
133
|
+
try {
|
|
134
|
+
await execAsync(`tar -tzf "${backupPath}" > /dev/null`);
|
|
135
|
+
verified = true;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// 验证失败,记录但不阻止
|
|
139
|
+
console.error(`[backup] 备份验证失败: ${backupPath}`);
|
|
140
|
+
}
|
|
141
|
+
// 获取文件大小
|
|
142
|
+
const stats = await fs.stat(backupPath);
|
|
143
|
+
// 创建备份元信息
|
|
144
|
+
const meta = {
|
|
145
|
+
name: backupName,
|
|
146
|
+
workspaceId,
|
|
147
|
+
workspaceName,
|
|
148
|
+
createdAt: new Date().toISOString(),
|
|
149
|
+
trigger,
|
|
150
|
+
codeVersion: this.getCurrentCodeVersion(),
|
|
151
|
+
size: stats.size,
|
|
152
|
+
verified,
|
|
153
|
+
};
|
|
154
|
+
// 更新元信息文件
|
|
155
|
+
const metas = await this.readMeta(projectRoot, wsDirName);
|
|
156
|
+
metas.push(meta);
|
|
157
|
+
await this.writeMeta(projectRoot, wsDirName, metas);
|
|
158
|
+
// 更新防抖时间戳
|
|
159
|
+
if (trigger === "auto") {
|
|
160
|
+
this.lastAutoBackup.set(workspaceId, Date.now());
|
|
161
|
+
}
|
|
162
|
+
// 轮转:超过最大数量时删除最旧的
|
|
163
|
+
await this.rotateBackups(projectRoot, wsDirName);
|
|
164
|
+
return meta;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 轮转备份:删除超出数量限制的旧备份
|
|
168
|
+
*/
|
|
169
|
+
async rotateBackups(projectRoot, wsDirName) {
|
|
170
|
+
const metas = await this.readMeta(projectRoot, wsDirName);
|
|
171
|
+
if (metas.length <= BackupService.MAX_BACKUPS) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
// 按时间排序,保留最新的
|
|
175
|
+
metas.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
176
|
+
const toDelete = metas.slice(BackupService.MAX_BACKUPS);
|
|
177
|
+
const toKeep = metas.slice(0, BackupService.MAX_BACKUPS);
|
|
178
|
+
// 删除旧备份文件
|
|
179
|
+
const backupDir = this.getBackupDir(projectRoot, wsDirName);
|
|
180
|
+
for (const meta of toDelete) {
|
|
181
|
+
const filePath = path.join(backupDir, meta.name);
|
|
182
|
+
try {
|
|
183
|
+
await fs.unlink(filePath);
|
|
184
|
+
console.log(`[backup] 轮转删除: ${meta.name}`);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// 删除失败不阻止
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// 更新元信息
|
|
191
|
+
await this.writeMeta(projectRoot, wsDirName, toKeep);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 列出工作区备份
|
|
195
|
+
*/
|
|
196
|
+
async listBackups(workspaceId) {
|
|
197
|
+
const wsEntry = await this.json.findWorkspaceEntry(workspaceId);
|
|
198
|
+
if (!wsEntry) {
|
|
199
|
+
throw new XtepError("WORKSPACE_NOT_FOUND", `工作区 "${workspaceId}" 不存在`);
|
|
200
|
+
}
|
|
201
|
+
const { projectRoot, dirName } = wsEntry;
|
|
202
|
+
const wsDirName = dirName || workspaceId;
|
|
203
|
+
const backupDir = this.getBackupDir(projectRoot, wsDirName);
|
|
204
|
+
const metas = await this.readMeta(projectRoot, wsDirName);
|
|
205
|
+
// 添加完整路径
|
|
206
|
+
return metas.map((meta) => ({
|
|
207
|
+
...meta,
|
|
208
|
+
path: path.join(backupDir, meta.name),
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 恢复工作区备份
|
|
213
|
+
* @param workspaceId 工作区 ID
|
|
214
|
+
* @param backupName 备份文件名
|
|
215
|
+
*/
|
|
216
|
+
async restoreBackup(workspaceId, backupName) {
|
|
217
|
+
const wsEntry = await this.json.findWorkspaceEntry(workspaceId);
|
|
218
|
+
if (!wsEntry) {
|
|
219
|
+
throw new XtepError("WORKSPACE_NOT_FOUND", `工作区 "${workspaceId}" 不存在`);
|
|
220
|
+
}
|
|
221
|
+
const { projectRoot, dirName } = wsEntry;
|
|
222
|
+
const wsDirName = dirName || workspaceId;
|
|
223
|
+
const backupDir = this.getBackupDir(projectRoot, wsDirName);
|
|
224
|
+
const backupPath = path.join(backupDir, backupName);
|
|
225
|
+
const workspacePath = this.fs.getWorkspacePath(projectRoot, wsDirName);
|
|
226
|
+
// 验证备份文件存在
|
|
227
|
+
if (!(await this.fs.exists(backupPath))) {
|
|
228
|
+
throw new XtepError("INVALID_PARAMS", `备份文件不存在: ${backupName}`);
|
|
229
|
+
}
|
|
230
|
+
// 恢复前先备份当前状态
|
|
231
|
+
await this.createBackup(workspaceId, "pre_operation");
|
|
232
|
+
// 清空工作区目录(保留 .backups)
|
|
233
|
+
const entries = await fs.readdir(workspacePath);
|
|
234
|
+
for (const entry of entries) {
|
|
235
|
+
if (entry === BackupService.BACKUP_DIR)
|
|
236
|
+
continue;
|
|
237
|
+
const entryPath = path.join(workspacePath, entry);
|
|
238
|
+
await fs.rm(entryPath, { recursive: true, force: true });
|
|
239
|
+
}
|
|
240
|
+
// 解压恢复
|
|
241
|
+
try {
|
|
242
|
+
await execAsync(`tar -xzf "${backupPath}" -C "${workspacePath}"`);
|
|
243
|
+
}
|
|
244
|
+
catch (e) {
|
|
245
|
+
throw new XtepError("INVALID_PARAMS", `备份恢复失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* 删除指定备份
|
|
250
|
+
*/
|
|
251
|
+
async deleteBackup(workspaceId, backupName) {
|
|
252
|
+
const wsEntry = await this.json.findWorkspaceEntry(workspaceId);
|
|
253
|
+
if (!wsEntry) {
|
|
254
|
+
throw new XtepError("WORKSPACE_NOT_FOUND", `工作区 "${workspaceId}" 不存在`);
|
|
255
|
+
}
|
|
256
|
+
const { projectRoot, dirName } = wsEntry;
|
|
257
|
+
const wsDirName = dirName || workspaceId;
|
|
258
|
+
const backupDir = this.getBackupDir(projectRoot, wsDirName);
|
|
259
|
+
const backupPath = path.join(backupDir, backupName);
|
|
260
|
+
// 删除文件
|
|
261
|
+
if (await this.fs.exists(backupPath)) {
|
|
262
|
+
await fs.unlink(backupPath);
|
|
263
|
+
}
|
|
264
|
+
// 更新元信息
|
|
265
|
+
const metas = await this.readMeta(projectRoot, wsDirName);
|
|
266
|
+
const filtered = metas.filter((m) => m.name !== backupName);
|
|
267
|
+
await this.writeMeta(projectRoot, wsDirName, filtered);
|
|
268
|
+
}
|
|
269
|
+
// ========== 全局备份方法 ==========
|
|
270
|
+
static GLOBAL_BACKUP_DIR = "backups";
|
|
271
|
+
static GLOBAL_BACKUP_FILES = [
|
|
272
|
+
"index.json",
|
|
273
|
+
"config.json",
|
|
274
|
+
"installation-meta.json",
|
|
275
|
+
];
|
|
276
|
+
/**
|
|
277
|
+
* 获取全局备份目录路径
|
|
278
|
+
*/
|
|
279
|
+
getGlobalBackupDir() {
|
|
280
|
+
return path.join(this.fs.getGlobalBasePath(), BackupService.GLOBAL_BACKUP_DIR);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* 生成全局备份文件名
|
|
284
|
+
*/
|
|
285
|
+
generateGlobalBackupName() {
|
|
286
|
+
// ISO 时间戳,替换不安全字符
|
|
287
|
+
const timestamp = new Date().toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
288
|
+
return `xtep-backup-${timestamp}.twbak`;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* 计算文件内容的 SHA256 校验和
|
|
292
|
+
*/
|
|
293
|
+
async calculateChecksum(filePaths) {
|
|
294
|
+
const hash = createHash("sha256");
|
|
295
|
+
for (const filePath of filePaths.sort()) {
|
|
296
|
+
try {
|
|
297
|
+
const content = await fs.readFile(filePath);
|
|
298
|
+
hash.update(content);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// 文件不存在时跳过,但记录日志
|
|
302
|
+
devLog.debug("[BackupService] checksum 跳过不存在的文件", { filePath });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return hash.digest("hex");
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* 创建全局备份
|
|
309
|
+
* @param trigger 触发类型
|
|
310
|
+
* @returns 备份信息
|
|
311
|
+
*/
|
|
312
|
+
async createGlobalBackup(trigger) {
|
|
313
|
+
devLog.debug("[BackupService] createGlobalBackup 开始", { trigger });
|
|
314
|
+
const globalBasePath = this.fs.getGlobalBasePath();
|
|
315
|
+
const backupDir = this.getGlobalBackupDir();
|
|
316
|
+
// 确保备份目录存在
|
|
317
|
+
await this.fs.ensureDir(backupDir);
|
|
318
|
+
devLog.debug("[BackupService] 备份目录已确保存在", { backupDir });
|
|
319
|
+
// 收集要备份的文件
|
|
320
|
+
const filesToBackup = [];
|
|
321
|
+
for (const fileName of BackupService.GLOBAL_BACKUP_FILES) {
|
|
322
|
+
const filePath = path.join(globalBasePath, fileName);
|
|
323
|
+
if (await this.fs.exists(filePath)) {
|
|
324
|
+
filesToBackup.push({ name: fileName, path: filePath });
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (filesToBackup.length === 0) {
|
|
328
|
+
throw new XtepError("INVALID_PARAMS", "没有可备份的文件");
|
|
329
|
+
}
|
|
330
|
+
devLog.debug("[BackupService] 找到待备份文件", { count: filesToBackup.length });
|
|
331
|
+
// 计算 checksum(备份前的源文件)
|
|
332
|
+
const checksum = await this.calculateChecksum(filesToBackup.map((f) => f.path));
|
|
333
|
+
devLog.debug("[BackupService] checksum 计算完成", { checksum: checksum.substring(0, 16) });
|
|
334
|
+
// 读取 index.json 获取工作区数量
|
|
335
|
+
let workspaceCount = 0;
|
|
336
|
+
const indexPath = path.join(globalBasePath, "index.json");
|
|
337
|
+
if (await this.fs.exists(indexPath)) {
|
|
338
|
+
try {
|
|
339
|
+
const indexContent = await this.fs.readFile(indexPath);
|
|
340
|
+
const indexData = JSON.parse(indexContent);
|
|
341
|
+
workspaceCount = indexData.workspaces?.length ?? 0;
|
|
342
|
+
}
|
|
343
|
+
catch {
|
|
344
|
+
// 解析失败时保持为 0
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
// 创建 manifest
|
|
348
|
+
const manifest = {
|
|
349
|
+
format: "twbak",
|
|
350
|
+
version: "1.0",
|
|
351
|
+
createdAt: new Date().toISOString(),
|
|
352
|
+
codeVersion: this.getCurrentCodeVersion(),
|
|
353
|
+
trigger,
|
|
354
|
+
checksum,
|
|
355
|
+
contents: {
|
|
356
|
+
workspaceCount,
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
// 生成备份文件名和路径
|
|
360
|
+
const backupName = this.generateGlobalBackupName();
|
|
361
|
+
const backupPath = path.join(backupDir, backupName);
|
|
362
|
+
devLog.debug("[BackupService] 备份文件路径", { backupPath });
|
|
363
|
+
// 创建 zip 包
|
|
364
|
+
await new Promise((resolve, reject) => {
|
|
365
|
+
const output = createWriteStream(backupPath);
|
|
366
|
+
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
367
|
+
output.on("close", () => {
|
|
368
|
+
devLog.debug("[BackupService] 压缩完成", { size: archive.pointer() });
|
|
369
|
+
resolve();
|
|
370
|
+
});
|
|
371
|
+
archive.on("error", (err) => {
|
|
372
|
+
devLog.error("[BackupService] 压缩失败", err);
|
|
373
|
+
reject(err);
|
|
374
|
+
});
|
|
375
|
+
archive.pipe(output);
|
|
376
|
+
// 添加 manifest.json
|
|
377
|
+
archive.append(JSON.stringify(manifest, null, 2), { name: "manifest.json" });
|
|
378
|
+
// 添加备份文件
|
|
379
|
+
for (const file of filesToBackup) {
|
|
380
|
+
archive.file(file.path, { name: file.name });
|
|
381
|
+
}
|
|
382
|
+
archive.finalize();
|
|
383
|
+
});
|
|
384
|
+
// 获取文件大小
|
|
385
|
+
const stats = await fs.stat(backupPath);
|
|
386
|
+
const result = {
|
|
387
|
+
name: backupName,
|
|
388
|
+
path: backupPath,
|
|
389
|
+
createdAt: manifest.createdAt,
|
|
390
|
+
codeVersion: manifest.codeVersion,
|
|
391
|
+
trigger,
|
|
392
|
+
size: stats.size,
|
|
393
|
+
};
|
|
394
|
+
devLog.debug("[BackupService] 全局备份创建成功", { backupName });
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* 列出全局备份
|
|
399
|
+
* @returns 备份列表,按时间倒序
|
|
400
|
+
*/
|
|
401
|
+
async listGlobalBackups() {
|
|
402
|
+
devLog.debug("[BackupService] listGlobalBackups 开始扫描全局备份");
|
|
403
|
+
const backupDir = this.getGlobalBackupDir();
|
|
404
|
+
// 检查备份目录是否存在
|
|
405
|
+
if (!(await this.fs.exists(backupDir))) {
|
|
406
|
+
devLog.debug("[BackupService] 备份目录不存在", { backupDir });
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
409
|
+
// 扫描 .twbak 文件
|
|
410
|
+
const entries = await fs.readdir(backupDir);
|
|
411
|
+
const backupFiles = entries.filter((e) => e.endsWith(".twbak"));
|
|
412
|
+
devLog.debug("[BackupService] 找到备份文件", { count: backupFiles.length });
|
|
413
|
+
const backups = [];
|
|
414
|
+
for (const fileName of backupFiles) {
|
|
415
|
+
const filePath = path.join(backupDir, fileName);
|
|
416
|
+
try {
|
|
417
|
+
// 读取 manifest
|
|
418
|
+
const zip = new AdmZip(filePath);
|
|
419
|
+
const manifestEntry = zip.getEntry("manifest.json");
|
|
420
|
+
if (!manifestEntry) {
|
|
421
|
+
devLog.warn("[BackupService] 备份文件缺少 manifest", { fileName });
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const manifestContent = manifestEntry.getData().toString("utf-8");
|
|
425
|
+
const manifest = JSON.parse(manifestContent);
|
|
426
|
+
// 获取文件大小
|
|
427
|
+
const stats = await fs.stat(filePath);
|
|
428
|
+
backups.push({
|
|
429
|
+
name: fileName,
|
|
430
|
+
path: filePath,
|
|
431
|
+
createdAt: manifest.createdAt,
|
|
432
|
+
codeVersion: manifest.codeVersion,
|
|
433
|
+
trigger: manifest.trigger,
|
|
434
|
+
size: stats.size,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
catch (err) {
|
|
438
|
+
devLog.warn("[BackupService] 读取备份文件失败", { fileName, error: String(err) });
|
|
439
|
+
// 跳过损坏的备份文件
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
// 按时间倒序排序
|
|
443
|
+
backups.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
444
|
+
devLog.debug("[BackupService] 返回有效备份", { count: backups.length });
|
|
445
|
+
return backups;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* 恢复全局备份
|
|
449
|
+
* @param backupPath 备份文件完整路径
|
|
450
|
+
* @returns 恢复前自动创建的 pre_restore 备份信息
|
|
451
|
+
*/
|
|
452
|
+
async restoreGlobalBackup(backupPath) {
|
|
453
|
+
devLog.debug("[BackupService] 开始恢复全局备份", { backupPath });
|
|
454
|
+
// 验证备份文件存在
|
|
455
|
+
if (!(await this.fs.exists(backupPath))) {
|
|
456
|
+
throw new XtepError("INVALID_PARAMS", "备份文件不存在");
|
|
457
|
+
}
|
|
458
|
+
// 验证文件扩展名
|
|
459
|
+
if (!backupPath.endsWith(".twbak")) {
|
|
460
|
+
throw new XtepError("INVALID_PARAMS", "无效的 .twbak 格式");
|
|
461
|
+
}
|
|
462
|
+
// 读取并验证 manifest
|
|
463
|
+
let zip;
|
|
464
|
+
let manifest;
|
|
465
|
+
try {
|
|
466
|
+
zip = new AdmZip(backupPath);
|
|
467
|
+
const manifestEntry = zip.getEntry("manifest.json");
|
|
468
|
+
if (!manifestEntry) {
|
|
469
|
+
throw new XtepError("INVALID_PARAMS", "无效的 .twbak 格式:缺少 manifest.json");
|
|
470
|
+
}
|
|
471
|
+
const manifestContent = manifestEntry.getData().toString("utf-8");
|
|
472
|
+
manifest = JSON.parse(manifestContent);
|
|
473
|
+
devLog.debug("[BackupService] manifest 解析成功", { version: manifest.version });
|
|
474
|
+
}
|
|
475
|
+
catch (err) {
|
|
476
|
+
if (err instanceof XtepError)
|
|
477
|
+
throw err;
|
|
478
|
+
throw new XtepError("INVALID_PARAMS", `无效的 .twbak 格式:${err instanceof Error ? err.message : String(err)}`);
|
|
479
|
+
}
|
|
480
|
+
// 提取文件到临时目录验证 checksum
|
|
481
|
+
const globalBasePath = this.fs.getGlobalBasePath();
|
|
482
|
+
const tempDir = path.join(globalBasePath, ".restore-temp");
|
|
483
|
+
try {
|
|
484
|
+
// 清理并创建临时目录
|
|
485
|
+
if (await this.fs.exists(tempDir)) {
|
|
486
|
+
await this.fs.remove(tempDir);
|
|
487
|
+
}
|
|
488
|
+
await this.fs.ensureDir(tempDir);
|
|
489
|
+
// 提取文件到临时目录
|
|
490
|
+
for (const fileName of BackupService.GLOBAL_BACKUP_FILES) {
|
|
491
|
+
const entry = zip.getEntry(fileName);
|
|
492
|
+
if (entry) {
|
|
493
|
+
const content = entry.getData();
|
|
494
|
+
await fs.writeFile(path.join(tempDir, fileName), content);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// 验证 checksum(calculateChecksum 内部会跳过不存在的文件)
|
|
498
|
+
const actualChecksum = await this.calculateChecksum(BackupService.GLOBAL_BACKUP_FILES.map((f) => path.join(tempDir, f)));
|
|
499
|
+
if (actualChecksum !== manifest.checksum) {
|
|
500
|
+
devLog.error("[BackupService] checksum 不匹配", undefined, { expected: manifest.checksum.substring(0, 16), actual: actualChecksum.substring(0, 16) });
|
|
501
|
+
throw new XtepError("INVALID_PARAMS", "备份文件损坏或被篡改");
|
|
502
|
+
}
|
|
503
|
+
devLog.debug("[BackupService] checksum 验证通过");
|
|
504
|
+
// 自动备份当前状态
|
|
505
|
+
devLog.debug("[BackupService] 恢复前自动备份当前状态");
|
|
506
|
+
const preRestoreBackup = await this.createGlobalBackup("pre_restore");
|
|
507
|
+
// 覆盖目标文件
|
|
508
|
+
for (const fileName of BackupService.GLOBAL_BACKUP_FILES) {
|
|
509
|
+
const tempFilePath = path.join(tempDir, fileName);
|
|
510
|
+
const targetFilePath = path.join(globalBasePath, fileName);
|
|
511
|
+
if (await this.fs.exists(tempFilePath)) {
|
|
512
|
+
const content = await fs.readFile(tempFilePath, "utf-8");
|
|
513
|
+
await this.fs.writeFile(targetFilePath, content);
|
|
514
|
+
devLog.debug("[BackupService] 已恢复文件", { fileName });
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
devLog.debug("[BackupService] 全局备份恢复成功");
|
|
518
|
+
return preRestoreBackup;
|
|
519
|
+
}
|
|
520
|
+
finally {
|
|
521
|
+
// 清理临时目录
|
|
522
|
+
if (await this.fs.exists(tempDir)) {
|
|
523
|
+
try {
|
|
524
|
+
await this.fs.remove(tempDir);
|
|
525
|
+
}
|
|
526
|
+
catch (err) {
|
|
527
|
+
devLog.warn("[BackupService] 清理临时目录失败", { tempDir, error: String(err) });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* 删除全局备份
|
|
534
|
+
* @param backupName 备份文件名
|
|
535
|
+
*/
|
|
536
|
+
async deleteGlobalBackup(backupName) {
|
|
537
|
+
devLog.debug("[BackupService] 删除全局备份", { backupName });
|
|
538
|
+
const backupDir = this.getGlobalBackupDir();
|
|
539
|
+
const backupPath = path.join(backupDir, backupName);
|
|
540
|
+
// 验证文件存在
|
|
541
|
+
if (!(await this.fs.exists(backupPath))) {
|
|
542
|
+
throw new XtepError("INVALID_PARAMS", "备份文件不存在");
|
|
543
|
+
}
|
|
544
|
+
// 删除文件
|
|
545
|
+
await fs.unlink(backupPath);
|
|
546
|
+
devLog.debug("[BackupService] 全局备份已删除", { backupName });
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
//# sourceMappingURL=BackupService.js.map
|