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,989 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* xtep-workspace rebuild 命令
|
|
4
|
+
* 索引管理:增量同步/完全重建/验证/备份还原
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, renameSync, copyFileSync, rmSync, statSync, } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join, dirname } from "path";
|
|
9
|
+
import { fileURLToPath } from "url";
|
|
10
|
+
import { createRequire } from "module";
|
|
11
|
+
// ES module 兼容
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = dirname(__filename);
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// 配置
|
|
17
|
+
// ============================================================================
|
|
18
|
+
const IS_DEV = process.env.NODE_ENV === "development" || process.env.XTEP_DEV === "true";
|
|
19
|
+
const HOME = homedir();
|
|
20
|
+
const FOLDER_NAME = IS_DEV ? ".xtep-workspace-dev" : ".xtep-workspace";
|
|
21
|
+
const GLOBAL_DIR = join(HOME, FOLDER_NAME);
|
|
22
|
+
const INDEX_PATH = join(GLOBAL_DIR, "index.json");
|
|
23
|
+
const BACKUPS_DIR = join(GLOBAL_DIR, "backups");
|
|
24
|
+
// 系统目录(不是工作区)
|
|
25
|
+
const SYSTEM_DIRS = ["scripts", "logs", "tutorial", "node_modules", "backups"];
|
|
26
|
+
// 最大保留备份数
|
|
27
|
+
const MAX_BACKUPS = 10;
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// 颜色输出
|
|
30
|
+
// ============================================================================
|
|
31
|
+
const colors = {
|
|
32
|
+
red: (s) => `\x1b[31m${s}\x1b[0m`,
|
|
33
|
+
green: (s) => `\x1b[32m${s}\x1b[0m`,
|
|
34
|
+
yellow: (s) => `\x1b[33m${s}\x1b[0m`,
|
|
35
|
+
blue: (s) => `\x1b[34m${s}\x1b[0m`,
|
|
36
|
+
gray: (s) => `\x1b[90m${s}\x1b[0m`,
|
|
37
|
+
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
38
|
+
};
|
|
39
|
+
function info(msg) {
|
|
40
|
+
console.log(`${colors.blue("[INFO]")} ${msg}`);
|
|
41
|
+
}
|
|
42
|
+
function success(msg) {
|
|
43
|
+
console.log(`${colors.green("[OK]")} ${msg}`);
|
|
44
|
+
}
|
|
45
|
+
function warn(msg) {
|
|
46
|
+
console.log(`${colors.yellow("[WARN]")} ${msg}`);
|
|
47
|
+
}
|
|
48
|
+
function error(msg) {
|
|
49
|
+
console.log(`${colors.red("[ERROR]")} ${msg}`);
|
|
50
|
+
}
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// 工具函数
|
|
53
|
+
// ============================================================================
|
|
54
|
+
function ensureDir(dir) {
|
|
55
|
+
if (!existsSync(dir)) {
|
|
56
|
+
mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function formatDate(date) {
|
|
60
|
+
return date.toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
61
|
+
}
|
|
62
|
+
function formatDateReadable(date) {
|
|
63
|
+
return date.toLocaleString("zh-CN", {
|
|
64
|
+
year: "numeric",
|
|
65
|
+
month: "2-digit",
|
|
66
|
+
day: "2-digit",
|
|
67
|
+
hour: "2-digit",
|
|
68
|
+
minute: "2-digit",
|
|
69
|
+
second: "2-digit",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function expandPath(p) {
|
|
73
|
+
if (p.startsWith("~")) {
|
|
74
|
+
return join(HOME, p.slice(1));
|
|
75
|
+
}
|
|
76
|
+
return p;
|
|
77
|
+
}
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// 索引读写
|
|
80
|
+
// ============================================================================
|
|
81
|
+
function readIndex() {
|
|
82
|
+
if (!existsSync(INDEX_PATH)) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
const content = readFileSync(INDEX_PATH, "utf-8");
|
|
87
|
+
return JSON.parse(content);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function writeIndex(index) {
|
|
94
|
+
ensureDir(GLOBAL_DIR);
|
|
95
|
+
const tmpPath = INDEX_PATH + ".tmp";
|
|
96
|
+
writeFileSync(tmpPath, JSON.stringify(index, null, 2), "utf-8");
|
|
97
|
+
renameSync(tmpPath, INDEX_PATH);
|
|
98
|
+
}
|
|
99
|
+
// ============================================================================
|
|
100
|
+
// 备份管理
|
|
101
|
+
// ============================================================================
|
|
102
|
+
function listBackups() {
|
|
103
|
+
if (!existsSync(BACKUPS_DIR)) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const files = readdirSync(BACKUPS_DIR)
|
|
107
|
+
.filter((f) => f.startsWith("index.") && f.endsWith(".json"))
|
|
108
|
+
.map((f) => {
|
|
109
|
+
const fullPath = join(BACKUPS_DIR, f);
|
|
110
|
+
const stat = statSync(fullPath);
|
|
111
|
+
// 从文件名提取日期: index.2024-12-27T00-48-30.json
|
|
112
|
+
const match = f.match(/index\.(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})\.json/);
|
|
113
|
+
const dateStr = match ? match[1].replace(/-/g, (m, i) => (i > 9 ? ":" : "-")) : "";
|
|
114
|
+
return {
|
|
115
|
+
name: f,
|
|
116
|
+
path: fullPath,
|
|
117
|
+
date: match ? new Date(dateStr.replace("T", " ").replace(/-/g, (_, i) => (i < 10 ? "-" : ":"))) : stat.mtime,
|
|
118
|
+
size: stat.size,
|
|
119
|
+
};
|
|
120
|
+
})
|
|
121
|
+
.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
122
|
+
return files;
|
|
123
|
+
}
|
|
124
|
+
function createBackup() {
|
|
125
|
+
if (!existsSync(INDEX_PATH)) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
ensureDir(BACKUPS_DIR);
|
|
129
|
+
const timestamp = formatDate(new Date());
|
|
130
|
+
const backupName = `index.${timestamp}.json`;
|
|
131
|
+
const backupPath = join(BACKUPS_DIR, backupName);
|
|
132
|
+
copyFileSync(INDEX_PATH, backupPath);
|
|
133
|
+
// 清理旧备份
|
|
134
|
+
const backups = listBackups();
|
|
135
|
+
if (backups.length > MAX_BACKUPS) {
|
|
136
|
+
for (const old of backups.slice(MAX_BACKUPS)) {
|
|
137
|
+
rmSync(old.path);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return backupName;
|
|
141
|
+
}
|
|
142
|
+
function restoreBackup(name) {
|
|
143
|
+
const backupPath = join(BACKUPS_DIR, name);
|
|
144
|
+
if (!existsSync(backupPath)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
// 验证备份文件有效
|
|
148
|
+
try {
|
|
149
|
+
const content = readFileSync(backupPath, "utf-8");
|
|
150
|
+
JSON.parse(content);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
// 当前索引备份(如果存在)
|
|
156
|
+
if (existsSync(INDEX_PATH)) {
|
|
157
|
+
createBackup();
|
|
158
|
+
}
|
|
159
|
+
copyFileSync(backupPath, INDEX_PATH);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
// ============================================================================
|
|
163
|
+
// 工作区备份管理
|
|
164
|
+
// ============================================================================
|
|
165
|
+
/**
|
|
166
|
+
* 获取工作区备份目录路径
|
|
167
|
+
*/
|
|
168
|
+
function getWorkspaceBackupDir(projectRoot, wsDirName) {
|
|
169
|
+
return join(projectRoot, FOLDER_NAME, wsDirName, ".backups");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 读取工作区备份元信息
|
|
173
|
+
*/
|
|
174
|
+
function readWorkspaceBackupMeta(projectRoot, wsDirName) {
|
|
175
|
+
const backupDir = getWorkspaceBackupDir(projectRoot, wsDirName);
|
|
176
|
+
const metaPath = join(backupDir, "backup-meta.json");
|
|
177
|
+
if (!existsSync(metaPath)) {
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const content = readFileSync(metaPath, "utf-8");
|
|
182
|
+
return JSON.parse(content);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* 写入工作区备份元信息
|
|
190
|
+
*/
|
|
191
|
+
function writeWorkspaceBackupMeta(projectRoot, wsDirName, metas) {
|
|
192
|
+
const backupDir = getWorkspaceBackupDir(projectRoot, wsDirName);
|
|
193
|
+
const metaPath = join(backupDir, "backup-meta.json");
|
|
194
|
+
ensureDir(backupDir);
|
|
195
|
+
writeFileSync(metaPath, JSON.stringify(metas, null, 2), "utf-8");
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 根据 workspaceId 查找工作区位置
|
|
199
|
+
*/
|
|
200
|
+
function findWorkspaceLocation(workspaceId) {
|
|
201
|
+
const index = readIndex();
|
|
202
|
+
if (!index)
|
|
203
|
+
return null;
|
|
204
|
+
const entry = index.workspaces.find(ws => ws.id === workspaceId);
|
|
205
|
+
if (!entry)
|
|
206
|
+
return null;
|
|
207
|
+
return {
|
|
208
|
+
projectRoot: entry.projectRoot,
|
|
209
|
+
dirName: entry.dirName || entry.id,
|
|
210
|
+
name: entry.name,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* 列出工作区备份
|
|
215
|
+
*/
|
|
216
|
+
function listWorkspaceBackups(workspaceId) {
|
|
217
|
+
const location = findWorkspaceLocation(workspaceId);
|
|
218
|
+
if (!location) {
|
|
219
|
+
error(`工作区不存在: ${workspaceId}`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
const { projectRoot, dirName, name } = location;
|
|
223
|
+
const metas = readWorkspaceBackupMeta(projectRoot, dirName);
|
|
224
|
+
console.log(`\n${colors.bold(`工作区备份列表: ${name}`)}`);
|
|
225
|
+
console.log(colors.gray("─".repeat(50)));
|
|
226
|
+
if (metas.length === 0) {
|
|
227
|
+
console.log(colors.yellow("暂无备份"));
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// 按时间倒序
|
|
231
|
+
metas.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
232
|
+
for (const meta of metas) {
|
|
233
|
+
const verified = meta.verified ? colors.green("✓") : colors.yellow("?");
|
|
234
|
+
const sizeKb = (meta.size / 1024).toFixed(1);
|
|
235
|
+
const date = new Date(meta.createdAt);
|
|
236
|
+
const triggerMap = {
|
|
237
|
+
manual: "手动",
|
|
238
|
+
auto: "自动",
|
|
239
|
+
pre_operation: "操作前",
|
|
240
|
+
};
|
|
241
|
+
console.log(` ${colors.blue(meta.name)} ${verified}`);
|
|
242
|
+
console.log(` 时间: ${formatDateReadable(date)} 大小: ${sizeKb} KB 触发: ${triggerMap[meta.trigger] || meta.trigger}`);
|
|
243
|
+
}
|
|
244
|
+
console.log(`\n${colors.gray("还原命令: xtep-workspace rebuild --restore-workspace " + workspaceId + " <备份名>")}\n`);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* 恢复工作区备份
|
|
248
|
+
*/
|
|
249
|
+
function restoreWorkspaceBackup(workspaceId, backupName) {
|
|
250
|
+
const location = findWorkspaceLocation(workspaceId);
|
|
251
|
+
if (!location) {
|
|
252
|
+
error(`工作区不存在: ${workspaceId}`);
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
const { projectRoot, dirName, name } = location;
|
|
256
|
+
const backupDir = getWorkspaceBackupDir(projectRoot, dirName);
|
|
257
|
+
const backupPath = join(backupDir, backupName);
|
|
258
|
+
const workspacePath = join(projectRoot, FOLDER_NAME, dirName);
|
|
259
|
+
// 1. 验证备份文件存在
|
|
260
|
+
if (!existsSync(backupPath)) {
|
|
261
|
+
error(`备份文件不存在: ${backupName}`);
|
|
262
|
+
console.log(colors.gray("提示: 使用 --list-ws-backups 查看可用备份"));
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
// 2. 验证备份完整性
|
|
266
|
+
info("验证备份完整性...");
|
|
267
|
+
try {
|
|
268
|
+
const { execSync } = require("child_process");
|
|
269
|
+
execSync(`tar -tzf "${backupPath}" > /dev/null`, { stdio: "pipe" });
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
error("备份文件损坏,无法恢复");
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
// 3. 创建恢复前备份
|
|
276
|
+
info("创建恢复前备份...");
|
|
277
|
+
const timestamp = formatDate(new Date());
|
|
278
|
+
const preRestoreBackupName = `backup_${timestamp}.tar.gz`;
|
|
279
|
+
const preRestoreBackupPath = join(backupDir, preRestoreBackupName);
|
|
280
|
+
try {
|
|
281
|
+
const { execSync } = require("child_process");
|
|
282
|
+
execSync(`tar -czf "${preRestoreBackupPath}" --exclude='.backups' -C "${workspacePath}" .`, { stdio: "pipe" });
|
|
283
|
+
// 更新元信息
|
|
284
|
+
const metas = readWorkspaceBackupMeta(projectRoot, dirName);
|
|
285
|
+
const stat = statSync(preRestoreBackupPath);
|
|
286
|
+
metas.push({
|
|
287
|
+
name: preRestoreBackupName,
|
|
288
|
+
workspaceId,
|
|
289
|
+
workspaceName: name,
|
|
290
|
+
createdAt: new Date().toISOString(),
|
|
291
|
+
trigger: "pre_operation",
|
|
292
|
+
codeVersion: "cli",
|
|
293
|
+
size: stat.size,
|
|
294
|
+
verified: true,
|
|
295
|
+
});
|
|
296
|
+
// 保留最多10个备份
|
|
297
|
+
if (metas.length > 10) {
|
|
298
|
+
const toDelete = metas.slice(0, metas.length - 10);
|
|
299
|
+
for (const old of toDelete) {
|
|
300
|
+
const oldPath = join(backupDir, old.name);
|
|
301
|
+
if (existsSync(oldPath)) {
|
|
302
|
+
rmSync(oldPath);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
metas.splice(0, metas.length - 10);
|
|
306
|
+
}
|
|
307
|
+
writeWorkspaceBackupMeta(projectRoot, dirName, metas);
|
|
308
|
+
success(`已创建恢复前备份: ${preRestoreBackupName}`);
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
warn(`创建恢复前备份失败: ${e instanceof Error ? e.message : e}`);
|
|
312
|
+
}
|
|
313
|
+
// 4. 清空工作区目录(保留 .backups)
|
|
314
|
+
info("清理工作区...");
|
|
315
|
+
try {
|
|
316
|
+
const entries = readdirSync(workspacePath);
|
|
317
|
+
for (const entry of entries) {
|
|
318
|
+
if (entry === ".backups")
|
|
319
|
+
continue;
|
|
320
|
+
const entryPath = join(workspacePath, entry);
|
|
321
|
+
rmSync(entryPath, { recursive: true, force: true });
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
catch (e) {
|
|
325
|
+
error(`清理失败: ${e instanceof Error ? e.message : e}`);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
// 5. 解压恢复
|
|
329
|
+
info("恢复备份...");
|
|
330
|
+
try {
|
|
331
|
+
const { execSync } = require("child_process");
|
|
332
|
+
execSync(`tar -xzf "${backupPath}" -C "${workspacePath}"`, { stdio: "pipe" });
|
|
333
|
+
success(`工作区 "${name}" 已成功恢复`);
|
|
334
|
+
}
|
|
335
|
+
catch (e) {
|
|
336
|
+
error(`恢复失败: ${e instanceof Error ? e.message : e}`);
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// ============================================================================
|
|
341
|
+
// 扫描和读取工作区
|
|
342
|
+
// ============================================================================
|
|
343
|
+
/**
|
|
344
|
+
* 从项目的 .xtep-workspace 目录读取工作区信息
|
|
345
|
+
*/
|
|
346
|
+
function readWorkspacesFromProject(projectRoot) {
|
|
347
|
+
const wsDir = join(projectRoot, FOLDER_NAME);
|
|
348
|
+
if (!existsSync(wsDir)) {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
const entries = [];
|
|
352
|
+
try {
|
|
353
|
+
const items = readdirSync(wsDir, { withFileTypes: true });
|
|
354
|
+
for (const item of items) {
|
|
355
|
+
if (!item.isDirectory() || SYSTEM_DIRS.includes(item.name)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
// 支持两种格式:config.json (项目工作区) 和 workspace.json (导出的工作区)
|
|
359
|
+
const configPath = join(wsDir, item.name, "config.json");
|
|
360
|
+
const workspacePath = join(wsDir, item.name, "workspace.json");
|
|
361
|
+
const actualPath = existsSync(configPath) ? configPath : existsSync(workspacePath) ? workspacePath : null;
|
|
362
|
+
if (!actualPath) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
const config = JSON.parse(readFileSync(actualPath, "utf-8"));
|
|
367
|
+
entries.push({
|
|
368
|
+
id: config.id || item.name,
|
|
369
|
+
name: config.name || item.name,
|
|
370
|
+
projectRoot: projectRoot,
|
|
371
|
+
status: config.status || "active",
|
|
372
|
+
createdAt: config.createdAt || new Date().toISOString(),
|
|
373
|
+
updatedAt: config.updatedAt || new Date().toISOString(),
|
|
374
|
+
dirName: item.name,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
warn(`无法读取工作区配置: ${configPath}`);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
warn(`无法扫描目录: ${wsDir}`);
|
|
384
|
+
}
|
|
385
|
+
return entries;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* 递归扫描目录,查找包含 .xtep-workspace 的项目
|
|
389
|
+
*/
|
|
390
|
+
function scanForProjects(rootPath, maxDepth = 3) {
|
|
391
|
+
const projects = [];
|
|
392
|
+
function scan(dir, depth) {
|
|
393
|
+
if (depth > maxDepth)
|
|
394
|
+
return;
|
|
395
|
+
// 检查当前目录是否有 .xtep-workspace
|
|
396
|
+
const wsDir = join(dir, FOLDER_NAME);
|
|
397
|
+
if (existsSync(wsDir)) {
|
|
398
|
+
projects.push(dir);
|
|
399
|
+
// 继续扫描子目录,因为可能有嵌套的独立项目
|
|
400
|
+
}
|
|
401
|
+
// 继续扫描子目录
|
|
402
|
+
try {
|
|
403
|
+
const items = readdirSync(dir, { withFileTypes: true });
|
|
404
|
+
for (const item of items) {
|
|
405
|
+
if (!item.isDirectory())
|
|
406
|
+
continue;
|
|
407
|
+
// 跳过隐藏目录和常见的不需要扫描的目录
|
|
408
|
+
if (item.name.startsWith(".") ||
|
|
409
|
+
item.name === "node_modules" ||
|
|
410
|
+
item.name === "dist" ||
|
|
411
|
+
item.name === "build" ||
|
|
412
|
+
item.name === "target") {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
scan(join(dir, item.name), depth + 1);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch {
|
|
419
|
+
// 忽略无法读取的目录
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
scan(rootPath, 0);
|
|
423
|
+
return projects;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* 验证工作区是否有效(目录存在且配置完整)
|
|
427
|
+
*/
|
|
428
|
+
function verifyWorkspace(entry) {
|
|
429
|
+
// 检查必要字段
|
|
430
|
+
if (!entry.id) {
|
|
431
|
+
return { valid: false, reason: "索引条目缺少 id 字段" };
|
|
432
|
+
}
|
|
433
|
+
if (!entry.projectRoot) {
|
|
434
|
+
return { valid: false, reason: "索引条目缺少 projectRoot 字段" };
|
|
435
|
+
}
|
|
436
|
+
// 辅助函数:读取并验证配置文件
|
|
437
|
+
const readAndValidateConfig = (wsPath) => {
|
|
438
|
+
const configPath = join(wsPath, "config.json");
|
|
439
|
+
const workspacePath = join(wsPath, "workspace.json");
|
|
440
|
+
const actualPath = existsSync(configPath) ? configPath : existsSync(workspacePath) ? workspacePath : null;
|
|
441
|
+
if (!actualPath) {
|
|
442
|
+
return { valid: false, reason: "config.json/workspace.json 不存在" };
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const config = JSON.parse(readFileSync(actualPath, "utf-8"));
|
|
446
|
+
if (!config.id || !config.name) {
|
|
447
|
+
return { valid: false, reason: "配置文件缺少必要字段" };
|
|
448
|
+
}
|
|
449
|
+
return { valid: true, config };
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
return { valid: false, reason: "配置文件无法解析" };
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
// 辅助函数:扫描项目目录查找匹配的工作区
|
|
456
|
+
const findMatchingWorkspace = (excludeDirName) => {
|
|
457
|
+
const wsDir = join(entry.projectRoot, FOLDER_NAME);
|
|
458
|
+
if (!existsSync(wsDir))
|
|
459
|
+
return null;
|
|
460
|
+
try {
|
|
461
|
+
const items = readdirSync(wsDir, { withFileTypes: true });
|
|
462
|
+
for (const item of items) {
|
|
463
|
+
if (!item.isDirectory() || SYSTEM_DIRS.includes(item.name))
|
|
464
|
+
continue;
|
|
465
|
+
if (excludeDirName && item.name === excludeDirName)
|
|
466
|
+
continue;
|
|
467
|
+
const itemPath = join(wsDir, item.name);
|
|
468
|
+
const result = readAndValidateConfig(itemPath);
|
|
469
|
+
if (result.valid && result.config.id === entry.id) {
|
|
470
|
+
return item.name;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
catch { /* ignore */ }
|
|
475
|
+
return null;
|
|
476
|
+
};
|
|
477
|
+
const originalDirName = entry.dirName;
|
|
478
|
+
// 如果 dirName 存在,先尝试直接验证
|
|
479
|
+
if (originalDirName) {
|
|
480
|
+
const wsPath = join(entry.projectRoot, FOLDER_NAME, originalDirName);
|
|
481
|
+
if (existsSync(wsPath)) {
|
|
482
|
+
const result = readAndValidateConfig(wsPath);
|
|
483
|
+
if (result.valid && result.config.id === entry.id) {
|
|
484
|
+
// 验证通过
|
|
485
|
+
return { valid: true };
|
|
486
|
+
}
|
|
487
|
+
// ID 不匹配或配置无效,继续尝试补全逻辑
|
|
488
|
+
}
|
|
489
|
+
// 目录不存在或验证失败,继续尝试补全逻辑
|
|
490
|
+
}
|
|
491
|
+
// dirName 缺失或验证失败,尝试从项目目录中查找匹配的工作区(排除已验证过的目录)
|
|
492
|
+
const foundDirName = findMatchingWorkspace(originalDirName);
|
|
493
|
+
if (foundDirName) {
|
|
494
|
+
return { valid: true, upgradedDirName: foundDirName };
|
|
495
|
+
}
|
|
496
|
+
return { valid: false, reason: originalDirName
|
|
497
|
+
? `目录 ${originalDirName} 无效且无法自动修复`
|
|
498
|
+
: "索引条目缺少 dirName 字段且无法自动补全"
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
// ============================================================================
|
|
502
|
+
// 核心操作
|
|
503
|
+
// ============================================================================
|
|
504
|
+
/**
|
|
505
|
+
* 增量同步:只添加新发现的工作区,保留现有的
|
|
506
|
+
*/
|
|
507
|
+
function incrementalSync(projectRoot, backup = true) {
|
|
508
|
+
const expandedPath = expandPath(projectRoot);
|
|
509
|
+
if (!existsSync(expandedPath)) {
|
|
510
|
+
error(`路径不存在: ${expandedPath}`);
|
|
511
|
+
return { added: 0, existing: 0 };
|
|
512
|
+
}
|
|
513
|
+
// 备份
|
|
514
|
+
if (backup) {
|
|
515
|
+
const backupName = createBackup();
|
|
516
|
+
if (backupName) {
|
|
517
|
+
info(`已创建备份: ${backupName}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// 读取现有索引
|
|
521
|
+
let index = readIndex();
|
|
522
|
+
if (!index) {
|
|
523
|
+
index = { version: "1.0", workspaces: [] };
|
|
524
|
+
}
|
|
525
|
+
// 从项目读取工作区
|
|
526
|
+
const foundWorkspaces = readWorkspacesFromProject(expandedPath);
|
|
527
|
+
let added = 0;
|
|
528
|
+
let existing = 0;
|
|
529
|
+
for (const ws of foundWorkspaces) {
|
|
530
|
+
const exists = index.workspaces.some((e) => e.id === ws.id || (e.projectRoot === ws.projectRoot && e.dirName === ws.dirName));
|
|
531
|
+
if (exists) {
|
|
532
|
+
existing++;
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
index.workspaces.push(ws);
|
|
536
|
+
added++;
|
|
537
|
+
success(`添加工作区: ${ws.name} (${ws.id})`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (added > 0) {
|
|
541
|
+
writeIndex(index);
|
|
542
|
+
}
|
|
543
|
+
return { added, existing };
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* 完全重建:清空并重新扫描
|
|
547
|
+
*/
|
|
548
|
+
function fullRebuild(projectRoot, backup = true) {
|
|
549
|
+
const expandedPath = expandPath(projectRoot);
|
|
550
|
+
if (!existsSync(expandedPath)) {
|
|
551
|
+
error(`路径不存在: ${expandedPath}`);
|
|
552
|
+
return { total: 0 };
|
|
553
|
+
}
|
|
554
|
+
// 备份
|
|
555
|
+
if (backup) {
|
|
556
|
+
const backupName = createBackup();
|
|
557
|
+
if (backupName) {
|
|
558
|
+
info(`已创建备份: ${backupName}`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// 从项目读取工作区
|
|
562
|
+
const foundWorkspaces = readWorkspacesFromProject(expandedPath);
|
|
563
|
+
// 创建新索引
|
|
564
|
+
const index = {
|
|
565
|
+
version: "1.0",
|
|
566
|
+
workspaces: foundWorkspaces,
|
|
567
|
+
};
|
|
568
|
+
writeIndex(index);
|
|
569
|
+
for (const ws of foundWorkspaces) {
|
|
570
|
+
success(`添加工作区: ${ws.name} (${ws.id})`);
|
|
571
|
+
}
|
|
572
|
+
return { total: foundWorkspaces.length };
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* 扫描目录并同步(递归查找项目)
|
|
576
|
+
*/
|
|
577
|
+
function scanAndSync(rootPath, backup = true) {
|
|
578
|
+
const expandedPath = expandPath(rootPath);
|
|
579
|
+
if (!existsSync(expandedPath)) {
|
|
580
|
+
error(`路径不存在: ${expandedPath}`);
|
|
581
|
+
return { projects: 0, workspaces: 0 };
|
|
582
|
+
}
|
|
583
|
+
info(`扫描目录: ${expandedPath}`);
|
|
584
|
+
const projects = scanForProjects(expandedPath);
|
|
585
|
+
if (projects.length === 0) {
|
|
586
|
+
warn("未找到包含 .xtep-workspace 的项目");
|
|
587
|
+
return { projects: 0, workspaces: 0 };
|
|
588
|
+
}
|
|
589
|
+
info(`找到 ${projects.length} 个项目`);
|
|
590
|
+
// 备份
|
|
591
|
+
if (backup) {
|
|
592
|
+
const backupName = createBackup();
|
|
593
|
+
if (backupName) {
|
|
594
|
+
info(`已创建备份: ${backupName}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
// 读取或创建索引
|
|
598
|
+
let index = readIndex();
|
|
599
|
+
if (!index) {
|
|
600
|
+
index = { version: "1.0", workspaces: [] };
|
|
601
|
+
}
|
|
602
|
+
let totalAdded = 0;
|
|
603
|
+
for (const project of projects) {
|
|
604
|
+
const workspaces = readWorkspacesFromProject(project);
|
|
605
|
+
for (const ws of workspaces) {
|
|
606
|
+
const exists = index.workspaces.some((e) => e.id === ws.id || (e.projectRoot === ws.projectRoot && e.dirName === ws.dirName));
|
|
607
|
+
if (!exists) {
|
|
608
|
+
index.workspaces.push(ws);
|
|
609
|
+
totalAdded++;
|
|
610
|
+
success(`添加: ${ws.name} @ ${project}`);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (totalAdded > 0) {
|
|
615
|
+
writeIndex(index);
|
|
616
|
+
}
|
|
617
|
+
return { projects: projects.length, workspaces: totalAdded };
|
|
618
|
+
}
|
|
619
|
+
function diagnoseIndex() {
|
|
620
|
+
const index = readIndex();
|
|
621
|
+
if (!index) {
|
|
622
|
+
warn("索引文件不存在");
|
|
623
|
+
return { total: 0, issues: [] };
|
|
624
|
+
}
|
|
625
|
+
const issues = [];
|
|
626
|
+
const seenIds = new Set();
|
|
627
|
+
const requiredFields = ["id", "name", "projectRoot", "dirName", "status"];
|
|
628
|
+
for (const ws of index.workspaces) {
|
|
629
|
+
const wsId = ws.id || "(无ID)";
|
|
630
|
+
const wsName = ws.name || "(无名称)";
|
|
631
|
+
// 检查必要字段
|
|
632
|
+
for (const field of requiredFields) {
|
|
633
|
+
const value = ws[field];
|
|
634
|
+
if (value === undefined || value === null) {
|
|
635
|
+
issues.push({
|
|
636
|
+
workspaceId: wsId,
|
|
637
|
+
workspaceName: wsName,
|
|
638
|
+
field,
|
|
639
|
+
issue: `字段缺失`,
|
|
640
|
+
severity: "error",
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
else if (typeof value !== "string") {
|
|
644
|
+
issues.push({
|
|
645
|
+
workspaceId: wsId,
|
|
646
|
+
workspaceName: wsName,
|
|
647
|
+
field,
|
|
648
|
+
issue: `类型错误 (期望 string, 实际 ${typeof value})`,
|
|
649
|
+
severity: "error",
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
else if (value === "") {
|
|
653
|
+
issues.push({
|
|
654
|
+
workspaceId: wsId,
|
|
655
|
+
workspaceName: wsName,
|
|
656
|
+
field,
|
|
657
|
+
issue: `字段为空`,
|
|
658
|
+
severity: field === "projectRoot" ? "error" : "warning",
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
// 检查 ID 重复
|
|
663
|
+
if (ws.id) {
|
|
664
|
+
if (seenIds.has(ws.id)) {
|
|
665
|
+
issues.push({
|
|
666
|
+
workspaceId: wsId,
|
|
667
|
+
workspaceName: wsName,
|
|
668
|
+
field: "id",
|
|
669
|
+
issue: `ID 重复`,
|
|
670
|
+
severity: "error",
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
seenIds.add(ws.id);
|
|
674
|
+
}
|
|
675
|
+
// 检查状态值
|
|
676
|
+
if (ws.status && !["active", "archived", "error"].includes(ws.status)) {
|
|
677
|
+
issues.push({
|
|
678
|
+
workspaceId: wsId,
|
|
679
|
+
workspaceName: wsName,
|
|
680
|
+
field: "status",
|
|
681
|
+
issue: `无效状态值: ${ws.status}`,
|
|
682
|
+
severity: "warning",
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
// 检查日期格式
|
|
686
|
+
for (const dateField of ["createdAt", "updatedAt"]) {
|
|
687
|
+
const value = ws[dateField];
|
|
688
|
+
if (value && typeof value === "string") {
|
|
689
|
+
const date = new Date(value);
|
|
690
|
+
if (isNaN(date.getTime())) {
|
|
691
|
+
issues.push({
|
|
692
|
+
workspaceId: wsId,
|
|
693
|
+
workspaceName: wsName,
|
|
694
|
+
field: dateField,
|
|
695
|
+
issue: `日期格式无效: ${value}`,
|
|
696
|
+
severity: "warning",
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return { total: index.workspaces.length, issues };
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* 验证并清理无效工作区
|
|
706
|
+
*/
|
|
707
|
+
function verifyAndClean(backup = true) {
|
|
708
|
+
const index = readIndex();
|
|
709
|
+
if (!index || index.workspaces.length === 0) {
|
|
710
|
+
info("索引为空,无需验证");
|
|
711
|
+
return { valid: 0, invalid: 0, upgraded: 0, removed: [] };
|
|
712
|
+
}
|
|
713
|
+
// 备份
|
|
714
|
+
if (backup) {
|
|
715
|
+
const backupName = createBackup();
|
|
716
|
+
if (backupName) {
|
|
717
|
+
info(`已创建备份: ${backupName}`);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
const validWorkspaces = [];
|
|
721
|
+
const removed = [];
|
|
722
|
+
let upgraded = 0;
|
|
723
|
+
for (const ws of index.workspaces) {
|
|
724
|
+
const result = verifyWorkspace(ws);
|
|
725
|
+
if (result.valid) {
|
|
726
|
+
// 如果有升级的 dirName,更新条目
|
|
727
|
+
if (result.upgradedDirName) {
|
|
728
|
+
ws.dirName = result.upgradedDirName;
|
|
729
|
+
upgraded++;
|
|
730
|
+
success(`升级工作区索引: ${ws.name} (补全 dirName: ${result.upgradedDirName})`);
|
|
731
|
+
}
|
|
732
|
+
validWorkspaces.push(ws);
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
removed.push(`${ws.name} (${ws.id}): ${result.reason}`);
|
|
736
|
+
warn(`移除无效工作区: ${ws.name} - ${result.reason}`);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
const invalid = index.workspaces.length - validWorkspaces.length;
|
|
740
|
+
// 有变更时写入索引
|
|
741
|
+
if (invalid > 0 || upgraded > 0) {
|
|
742
|
+
index.workspaces = validWorkspaces;
|
|
743
|
+
writeIndex(index);
|
|
744
|
+
}
|
|
745
|
+
return { valid: validWorkspaces.length, invalid, upgraded, removed };
|
|
746
|
+
}
|
|
747
|
+
// ============================================================================
|
|
748
|
+
// 显示函数
|
|
749
|
+
// ============================================================================
|
|
750
|
+
function showHelp() {
|
|
751
|
+
console.log(`
|
|
752
|
+
${colors.bold("XtepWorkspace 索引管理工具")}
|
|
753
|
+
|
|
754
|
+
${colors.blue("用法:")}
|
|
755
|
+
xtep-workspace rebuild 显示帮助和当前状态
|
|
756
|
+
xtep-workspace rebuild <path> 增量同步指定项目
|
|
757
|
+
xtep-workspace rebuild --full <path> 完全重建(清空后重新扫描)
|
|
758
|
+
xtep-workspace rebuild --scan <path> 递归扫描目录查找项目
|
|
759
|
+
xtep-workspace rebuild --verify 验证并清理无效工作区
|
|
760
|
+
xtep-workspace rebuild --diagnose 诊断索引问题(不修改数据)
|
|
761
|
+
xtep-workspace rebuild --list 列出所有索引备份
|
|
762
|
+
xtep-workspace rebuild --restore <name> 还原索引备份
|
|
763
|
+
xtep-workspace rebuild --list-ws-backups <id> 列出工作区备份
|
|
764
|
+
xtep-workspace rebuild --restore-workspace <id> <backup> 恢复工作区备份
|
|
765
|
+
|
|
766
|
+
${colors.blue("选项:")}
|
|
767
|
+
--no-backup 不创建备份
|
|
768
|
+
|
|
769
|
+
${colors.blue("示例:")}
|
|
770
|
+
xtep-workspace rebuild ~/projects/myapp 同步单个项目
|
|
771
|
+
xtep-workspace rebuild --scan ~/projects 扫描多个项目
|
|
772
|
+
xtep-workspace rebuild --full ~/projects/myapp 完全重建
|
|
773
|
+
xtep-workspace rebuild --verify 清理无效条目
|
|
774
|
+
xtep-workspace rebuild --list 查看备份列表
|
|
775
|
+
xtep-workspace rebuild --restore index.2024-12-27T10-30-00.json
|
|
776
|
+
`);
|
|
777
|
+
}
|
|
778
|
+
function showStatus() {
|
|
779
|
+
console.log(`\n${colors.bold("当前索引状态")}`);
|
|
780
|
+
console.log(colors.gray("─".repeat(50)));
|
|
781
|
+
const index = readIndex();
|
|
782
|
+
if (!index) {
|
|
783
|
+
console.log(`${colors.yellow("索引文件:")} 不存在`);
|
|
784
|
+
console.log(`${colors.gray("路径:")} ${INDEX_PATH}`);
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
console.log(`${colors.green("索引文件:")} 存在`);
|
|
788
|
+
console.log(`${colors.gray("路径:")} ${INDEX_PATH}`);
|
|
789
|
+
console.log(`${colors.blue("工作区数量:")} ${index.workspaces.length}`);
|
|
790
|
+
if (index.workspaces.length > 0) {
|
|
791
|
+
console.log(`\n${colors.bold("工作区列表:")}`);
|
|
792
|
+
const grouped = new Map();
|
|
793
|
+
for (const ws of index.workspaces) {
|
|
794
|
+
const list = grouped.get(ws.projectRoot) || [];
|
|
795
|
+
list.push(ws);
|
|
796
|
+
grouped.set(ws.projectRoot, list);
|
|
797
|
+
}
|
|
798
|
+
for (const [project, workspaces] of grouped) {
|
|
799
|
+
console.log(` ${colors.blue(project)}`);
|
|
800
|
+
for (const ws of workspaces) {
|
|
801
|
+
const statusColor = ws.status === "active" ? colors.green : colors.gray;
|
|
802
|
+
console.log(` - ${ws.name} ${statusColor(`[${ws.status}]`)}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
// 备份信息
|
|
808
|
+
const backups = listBackups();
|
|
809
|
+
console.log(`\n${colors.bold("备份信息")}`);
|
|
810
|
+
console.log(colors.gray("─".repeat(50)));
|
|
811
|
+
console.log(`${colors.blue("备份目录:")} ${BACKUPS_DIR}`);
|
|
812
|
+
console.log(`${colors.blue("备份数量:")} ${backups.length}/${MAX_BACKUPS}`);
|
|
813
|
+
if (backups.length > 0) {
|
|
814
|
+
console.log(`${colors.blue("最近备份:")} ${backups[0].name}`);
|
|
815
|
+
console.log(`${colors.gray("时间:")} ${formatDateReadable(backups[0].date)}`);
|
|
816
|
+
}
|
|
817
|
+
console.log();
|
|
818
|
+
}
|
|
819
|
+
function showBackupList() {
|
|
820
|
+
const backups = listBackups();
|
|
821
|
+
console.log(`\n${colors.bold("索引备份列表")}`);
|
|
822
|
+
console.log(colors.gray("─".repeat(60)));
|
|
823
|
+
if (backups.length === 0) {
|
|
824
|
+
console.log(colors.yellow("暂无备份"));
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
for (let i = 0; i < backups.length; i++) {
|
|
828
|
+
const b = backups[i];
|
|
829
|
+
const sizeKb = (b.size / 1024).toFixed(1);
|
|
830
|
+
const marker = i === 0 ? colors.green(" (最新)") : "";
|
|
831
|
+
console.log(` ${colors.blue(b.name)}${marker}`);
|
|
832
|
+
console.log(` 时间: ${formatDateReadable(b.date)} 大小: ${sizeKb} KB`);
|
|
833
|
+
}
|
|
834
|
+
console.log(`\n${colors.gray("还原命令: xtep-workspace rebuild --restore <备份名>")}\n`);
|
|
835
|
+
}
|
|
836
|
+
// ============================================================================
|
|
837
|
+
// 主函数
|
|
838
|
+
// ============================================================================
|
|
839
|
+
// ============================================================================
|
|
840
|
+
// 导出供 HTTP API 使用的函数
|
|
841
|
+
// ============================================================================
|
|
842
|
+
export { incrementalSync, scanAndSync, verifyAndClean, readWorkspacesFromProject, verifyWorkspace, readIndex, writeIndex, scanForProjects, };
|
|
843
|
+
export default function main() {
|
|
844
|
+
const args = process.argv.slice(3); // 跳过 node, script, "rebuild"
|
|
845
|
+
// 无参数 → 显示帮助和状态
|
|
846
|
+
if (args.length === 0) {
|
|
847
|
+
showHelp();
|
|
848
|
+
showStatus();
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
// 解析参数
|
|
852
|
+
const noBackup = args.includes("--no-backup");
|
|
853
|
+
const filteredArgs = args.filter((a) => a !== "--no-backup");
|
|
854
|
+
const command = filteredArgs[0];
|
|
855
|
+
const param = filteredArgs[1];
|
|
856
|
+
switch (command) {
|
|
857
|
+
case "--help":
|
|
858
|
+
case "-h":
|
|
859
|
+
showHelp();
|
|
860
|
+
break;
|
|
861
|
+
case "--list":
|
|
862
|
+
case "-l":
|
|
863
|
+
showBackupList();
|
|
864
|
+
break;
|
|
865
|
+
case "--restore":
|
|
866
|
+
case "-r":
|
|
867
|
+
if (!param) {
|
|
868
|
+
error("请指定要还原的备份名称");
|
|
869
|
+
console.log(colors.gray("提示: 使用 --list 查看可用备份"));
|
|
870
|
+
process.exit(1);
|
|
871
|
+
}
|
|
872
|
+
if (restoreBackup(param)) {
|
|
873
|
+
success(`已还原备份: ${param}`);
|
|
874
|
+
}
|
|
875
|
+
else {
|
|
876
|
+
error(`还原失败: 备份不存在或无效`);
|
|
877
|
+
process.exit(1);
|
|
878
|
+
}
|
|
879
|
+
break;
|
|
880
|
+
case "--verify":
|
|
881
|
+
case "-v":
|
|
882
|
+
info("验证索引中的工作区...");
|
|
883
|
+
const verifyResult = verifyAndClean(!noBackup);
|
|
884
|
+
console.log();
|
|
885
|
+
let summaryParts = [`${verifyResult.valid} 有效`, `${verifyResult.invalid} 无效`];
|
|
886
|
+
if (verifyResult.upgraded > 0) {
|
|
887
|
+
summaryParts.push(`${verifyResult.upgraded} 已升级`);
|
|
888
|
+
}
|
|
889
|
+
success(`验证完成: ${summaryParts.join(", ")}`);
|
|
890
|
+
if (verifyResult.removed.length > 0) {
|
|
891
|
+
console.log(colors.yellow("\n已移除的工作区:"));
|
|
892
|
+
for (const r of verifyResult.removed) {
|
|
893
|
+
console.log(` - ${r}`);
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
break;
|
|
897
|
+
case "--diagnose":
|
|
898
|
+
case "-d":
|
|
899
|
+
info("诊断索引问题...\n");
|
|
900
|
+
const diagnoseResult = diagnoseIndex();
|
|
901
|
+
if (diagnoseResult.total === 0) {
|
|
902
|
+
warn("索引为空");
|
|
903
|
+
break;
|
|
904
|
+
}
|
|
905
|
+
console.log(`${colors.blue("工作区总数:")} ${diagnoseResult.total}`);
|
|
906
|
+
if (diagnoseResult.issues.length === 0) {
|
|
907
|
+
console.log();
|
|
908
|
+
success("未发现问题,索引数据完整");
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
const errors = diagnoseResult.issues.filter((i) => i.severity === "error");
|
|
912
|
+
const warnings = diagnoseResult.issues.filter((i) => i.severity === "warning");
|
|
913
|
+
console.log(`${colors.red("错误:")} ${errors.length} ${colors.yellow("警告:")} ${warnings.length}\n`);
|
|
914
|
+
if (errors.length > 0) {
|
|
915
|
+
console.log(colors.red("=== 错误 ==="));
|
|
916
|
+
for (const issue of errors) {
|
|
917
|
+
console.log(` ${colors.red("✗")} [${issue.workspaceName}] ${issue.field}: ${issue.issue}`);
|
|
918
|
+
}
|
|
919
|
+
console.log();
|
|
920
|
+
}
|
|
921
|
+
if (warnings.length > 0) {
|
|
922
|
+
console.log(colors.yellow("=== 警告 ==="));
|
|
923
|
+
for (const issue of warnings) {
|
|
924
|
+
console.log(` ${colors.yellow("!")} [${issue.workspaceName}] ${issue.field}: ${issue.issue}`);
|
|
925
|
+
}
|
|
926
|
+
console.log();
|
|
927
|
+
}
|
|
928
|
+
if (errors.length > 0) {
|
|
929
|
+
console.log(colors.gray("提示: 使用 --verify 清理无效条目,或手动修复索引文件"));
|
|
930
|
+
console.log(colors.gray(`索引路径: ${INDEX_PATH}`));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
break;
|
|
934
|
+
case "--scan":
|
|
935
|
+
case "-s":
|
|
936
|
+
if (!param) {
|
|
937
|
+
error("请指定要扫描的目录");
|
|
938
|
+
process.exit(1);
|
|
939
|
+
}
|
|
940
|
+
info(`递归扫描: ${param}`);
|
|
941
|
+
const scanResult = scanAndSync(param, !noBackup);
|
|
942
|
+
console.log();
|
|
943
|
+
success(`扫描完成: ${scanResult.projects} 个项目, ${scanResult.workspaces} 个新工作区`);
|
|
944
|
+
break;
|
|
945
|
+
case "--full":
|
|
946
|
+
case "-f":
|
|
947
|
+
if (!param) {
|
|
948
|
+
error("请指定项目路径");
|
|
949
|
+
process.exit(1);
|
|
950
|
+
}
|
|
951
|
+
info(`完全重建: ${param}`);
|
|
952
|
+
const fullResult = fullRebuild(param, !noBackup);
|
|
953
|
+
console.log();
|
|
954
|
+
success(`重建完成: ${fullResult.total} 个工作区`);
|
|
955
|
+
break;
|
|
956
|
+
case "--list-ws-backups":
|
|
957
|
+
case "-lwb":
|
|
958
|
+
if (!param) {
|
|
959
|
+
error("请指定工作区 ID");
|
|
960
|
+
process.exit(1);
|
|
961
|
+
}
|
|
962
|
+
listWorkspaceBackups(param);
|
|
963
|
+
break;
|
|
964
|
+
case "--restore-workspace":
|
|
965
|
+
case "-rw": {
|
|
966
|
+
if (!param) {
|
|
967
|
+
error("请指定工作区 ID");
|
|
968
|
+
process.exit(1);
|
|
969
|
+
}
|
|
970
|
+
const backupName = filteredArgs[2];
|
|
971
|
+
if (!backupName) {
|
|
972
|
+
error("请指定备份文件名");
|
|
973
|
+
console.log(colors.gray(`提示: 使用 --list-ws-backups ${param} 查看可用备份`));
|
|
974
|
+
process.exit(1);
|
|
975
|
+
}
|
|
976
|
+
restoreWorkspaceBackup(param, backupName);
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
default:
|
|
980
|
+
// 默认:增量同步
|
|
981
|
+
const path = command;
|
|
982
|
+
info(`增量同步: ${path}`);
|
|
983
|
+
const syncResult = incrementalSync(path, !noBackup);
|
|
984
|
+
console.log();
|
|
985
|
+
success(`同步完成: ${syncResult.added} 新增, ${syncResult.existing} 已存在`);
|
|
986
|
+
break;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
//# sourceMappingURL=rebuild.js.map
|