dsh-config-manager 0.1.71 → 0.1.73
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/README.md +149 -98
- package/README.zh-CN.md +245 -88
- package/lib/adapters/agent-instructions.js +1 -1
- package/lib/adapters/file-collection.js +1 -1
- package/lib/adapters/index.d.ts +2 -1
- package/lib/adapters/index.js +6 -1
- package/lib/adapters/link-report.d.ts +1 -0
- package/lib/adapters/link-report.js +27 -3
- package/lib/adapters/messages.d.ts +21 -0
- package/lib/adapters/messages.js +57 -0
- package/lib/adapters/plugin-data.d.ts +114 -0
- package/lib/adapters/plugin-data.js +309 -0
- package/lib/adapters/plugin-files.js +3 -2
- package/lib/adapters/plugins.js +4 -3
- package/lib/adapters/prompts.d.ts +1 -0
- package/lib/adapters/prompts.js +7 -1
- package/lib/adapters/self.d.ts +8 -2
- package/lib/adapters/self.js +3 -2
- package/lib/adapters/sessions.d.ts +1 -0
- package/lib/adapters/sessions.js +4 -2
- package/lib/adapters/test-helpers.d.ts +9 -0
- package/lib/adapters/test-helpers.js +14 -0
- package/lib/adapters/ui.d.ts +2 -2
- package/lib/adapters/ui.js +25 -25
- package/lib/adapters/workspaces.d.ts +1 -0
- package/lib/adapters/workspaces.js +7 -3
- package/lib/cli/actions.d.ts +9 -1
- package/lib/cli/actions.js +5 -2
- package/lib/cli/help.js +23 -10
- package/lib/cli/index.d.ts +23 -0
- package/lib/cli/index.js +90 -32
- package/lib/cli/sessions-inspect.js +3 -1
- package/lib/cli/sessions-repair.js +25 -6
- package/lib/cli/web/page.d.ts +12 -0
- package/lib/cli/web/page.js +21 -12
- package/lib/cli/web/routes.js +2 -2
- package/lib/cli/web/server.js +4 -4
- package/lib/client.d.ts +1442 -494
- package/lib/client.js +15326 -8520
- package/lib/core/analyzer.d.ts +11 -2
- package/lib/core/analyzer.js +31 -8
- package/lib/core/backup-plan.d.ts +10 -0
- package/lib/core/backup-plan.js +51 -20
- package/lib/core/backup-verify.js +24 -23
- package/lib/core/backup.d.ts +25 -1
- package/lib/core/backup.js +99 -28
- package/lib/core/boot-rescue.js +4 -3
- package/lib/core/boot-safety.js +2 -1
- package/lib/core/command-rollback.d.ts +182 -0
- package/lib/core/command-rollback.js +293 -0
- package/lib/core/consult-source.js +7 -6
- package/lib/core/disk-usage.d.ts +74 -10
- package/lib/core/disk-usage.js +67 -4
- package/lib/core/engine-locale.d.ts +87 -0
- package/lib/core/engine-locale.js +125 -0
- package/lib/core/exporter.js +3 -3
- package/lib/core/fork-reconcile.d.ts +128 -0
- package/lib/core/fork-reconcile.js +150 -0
- package/lib/core/journal.js +6 -5
- package/lib/core/local-plugin-host.js +3 -2
- package/lib/core/local-plugin-pack.js +11 -6
- package/lib/core/messages-core.d.ts +294 -0
- package/lib/core/messages-core.js +619 -0
- package/lib/core/messages.d.ts +61 -2
- package/lib/core/messages.js +118 -13
- package/lib/core/migration-consult.js +21 -20
- package/lib/core/migration-history.js +19 -5
- package/lib/core/model-tools.d.ts +0 -22
- package/lib/core/model-tools.js +50 -27
- package/lib/core/phase3-host.js +30 -5
- package/lib/core/plugin-cli.js +18 -19
- package/lib/core/plugin-data-registry.d.ts +124 -0
- package/lib/core/plugin-data-registry.js +198 -0
- package/lib/core/reconcile.d.ts +2 -14
- package/lib/core/reconcile.js +87 -17
- package/lib/core/recovery-orchestrator.js +19 -18
- package/lib/core/reinstall.d.ts +4 -0
- package/lib/core/reinstall.js +26 -21
- package/lib/core/restore.js +20 -17
- package/lib/core/rollback-gate.d.ts +33 -0
- package/lib/core/rollback-gate.js +57 -0
- package/lib/core/rollback-ignore-rules.d.ts +31 -0
- package/lib/core/rollback-ignore-rules.js +140 -0
- package/lib/core/rollback-ledger.d.ts +248 -0
- package/lib/core/rollback-ledger.js +1282 -0
- package/lib/core/rollback-point.d.ts +1391 -0
- package/lib/core/rollback-point.js +3503 -0
- package/lib/core/rollback.js +6 -4
- package/lib/core/run-registry.js +6 -5
- package/lib/core/session-health.d.ts +15 -0
- package/lib/core/session-meta.d.ts +42 -2
- package/lib/core/session-meta.js +140 -1
- package/lib/core/session-title.d.ts +28 -0
- package/lib/core/session-title.js +185 -0
- package/lib/core/startup-barrier.d.ts +24 -0
- package/lib/core/startup-barrier.js +22 -0
- package/lib/core/transaction-coordinator.js +4 -3
- package/lib/core/types.d.ts +37 -1
- package/lib/core/validator.js +5 -4
- package/lib/core/verify-recovery.js +31 -30
- package/lib/foreign/bundle.js +3 -2
- package/lib/foreign/continue.js +2 -1
- package/lib/foreign/crush.js +2 -1
- package/lib/foreign/gemini.js +2 -1
- package/lib/foreign/goose.js +2 -1
- package/lib/foreign/kilocode.js +2 -1
- package/lib/foreign/kimi.js +2 -1
- package/lib/foreign/messages.d.ts +15 -0
- package/lib/foreign/messages.js +40 -0
- package/lib/foreign/mimocode.js +2 -1
- package/lib/foreign/openclaw.js +2 -1
- package/lib/foreign/opencode.js +2 -1
- package/lib/foreign/pi.js +2 -1
- package/lib/foreign/qoder.js +2 -1
- package/lib/foreign/qwen.js +2 -1
- package/lib/foreign/registry.d.ts +1 -1
- package/lib/foreign/registry.js +8 -7
- package/lib/foreign/teleagent.js +2 -1
- package/lib/foreign/workbuddy.js +2 -1
- package/lib/foreign/zcode.js +2 -1
- package/lib/foreign/zed.js +2 -1
- package/lib/index.d.ts +227 -7
- package/lib/index.js +799 -54
- package/lib/market/git-file-writer.js +20 -19
- package/lib/market/github-repos.js +35 -19
- package/lib/market/index-parser.js +31 -30
- package/lib/market/messages-market.d.ts +138 -0
- package/lib/market/messages-market.js +305 -0
- package/lib/market/my-repo.d.ts +2 -2
- package/lib/market/my-repo.js +32 -29
- package/lib/market/prepare.js +18 -15
- package/lib/market/security.js +20 -19
- package/lib/market/types.js +2 -1
- package/lib/market/url.js +2 -1
- package/lib/migrations/index.js +6 -5
- package/lib/migrations/messages-migrations.d.ts +32 -0
- package/lib/migrations/messages-migrations.js +58 -0
- package/lib/profiles/dsh-profile-launcher.js +2 -1
- package/lib/profiles/dsh-profile-runtime.js +2 -1
- package/lib/profiles/messages.d.ts +7 -0
- package/lib/profiles/messages.js +27 -0
- package/lib/routes/backup.js +18 -2
- package/lib/routes/import.js +3 -3
- package/lib/routes/index.d.ts +1 -1
- package/lib/routes/kit.d.ts +3 -2
- package/lib/routes/kit.js +6 -1
- package/lib/routes/market.js +1 -1
- package/lib/routes/messages.d.ts +43 -0
- package/lib/routes/messages.js +102 -0
- package/lib/routes/prefs.js +26 -3
- package/lib/routes/profiles.js +12 -6
- package/lib/routes/recovery.d.ts +21 -0
- package/lib/routes/recovery.js +478 -94
- package/lib/routes/snapshots.js +51 -4
- package/lib/routes/sync.js +79 -17
- package/lib/schema/config.d.ts +11 -1
- package/lib/schema/config.js +52 -20
- package/lib/schema/index.d.ts +8 -6
- package/lib/schema/index.js +8 -6
- package/lib/schema/manifest.js +19 -18
- package/lib/schema/messages.d.ts +47 -0
- package/lib/schema/messages.js +110 -0
- package/lib/schema/section-registry.d.ts +42 -15
- package/lib/schema/section-registry.js +67 -32
- package/lib/schema/types.d.ts +1 -1
- package/lib/schema/versions.js +6 -5
- package/lib/security/encryption.js +16 -15
- package/lib/security/integrity.js +8 -7
- package/lib/security/messages.d.ts +30 -0
- package/lib/security/messages.js +77 -0
- package/lib/security/redaction.js +12 -0
- package/lib/security/secret-scanner.js +16 -13
- package/lib/security/vault.js +2 -1
- package/lib/security/zip-security.js +4 -3
- package/lib/sync/ancestor.js +2 -1
- package/lib/sync/autosync-config.d.ts +11 -0
- package/lib/sync/autosync-config.js +12 -0
- package/lib/sync/autosync-scheduler.d.ts +11 -1
- package/lib/sync/autosync-scheduler.js +115 -76
- package/lib/sync/backup-files.js +3 -2
- package/lib/sync/backup-scheduler.js +6 -5
- package/lib/sync/blob-store.js +2 -1
- package/lib/sync/git/git-transport.d.ts +55 -0
- package/lib/sync/git/git-transport.js +152 -2
- package/lib/sync/github-auth.js +10 -9
- package/lib/sync/layout.js +13 -12
- package/lib/sync/messages.d.ts +63 -0
- package/lib/sync/messages.js +149 -0
- package/lib/sync/review-queue.js +6 -5
- package/lib/sync/s3/messages.d.ts +7 -0
- package/lib/sync/s3/messages.js +16 -0
- package/lib/sync/s3/s3-providers.d.ts +4 -1
- package/lib/sync/s3/s3-providers.js +5 -5
- package/lib/sync/s3/s3-transport.js +1 -1
- package/lib/sync/s3/sigv4.js +2 -1
- package/lib/sync/snapshot-crypto.js +6 -5
- package/lib/sync/snapshot-json.js +3 -2
- package/lib/sync/sync-engine.d.ts +58 -22
- package/lib/sync/sync-engine.js +119 -48
- package/lib/sync/sync-history.js +7 -6
- package/lib/sync/sync-selection.d.ts +15 -4
- package/lib/sync/sync-selection.js +17 -5
- package/lib/sync/ui-prefs.d.ts +22 -0
- package/lib/sync/ui-prefs.js +28 -0
- package/lib/sync/webdav/webdav-transport.d.ts +1 -0
- package/lib/sync/webdav/webdav-transport.js +4 -1
- package/lib/ui/artifact-view.js +4 -3
- package/lib/ui/commands.d.ts +1 -1
- package/lib/ui/commands.js +10 -0
- package/lib/ui/diff-view.d.ts +10 -0
- package/lib/ui/diff-view.js +17 -0
- package/lib/ui/disk-usage-view.d.ts +33 -4
- package/lib/ui/disk-usage-view.js +29 -1
- package/lib/ui/errors.d.ts +1 -1
- package/lib/ui/errors.js +4 -3
- package/lib/ui/export-flow.js +26 -20
- package/lib/ui/foreign-view.d.ts +5 -1
- package/lib/ui/foreign-view.js +26 -2
- package/lib/ui/i18n.d.ts +68 -1
- package/lib/ui/i18n.js +120 -2
- package/lib/ui/import-wizard.d.ts +7 -0
- package/lib/ui/import-wizard.js +13 -10
- package/lib/ui/progress.d.ts +2 -2
- package/lib/ui/progress.js +1 -2
- package/lib/ui/report.d.ts +5 -5
- package/lib/ui/report.js +6 -6
- package/lib/ui/rollback-view.d.ts +815 -0
- package/lib/ui/rollback-view.js +738 -0
- package/lib/ui/selection-model.d.ts +41 -0
- package/lib/ui/selection-model.js +30 -0
- package/lib/ui/session-inventory-view.d.ts +97 -3
- package/lib/ui/session-inventory-view.js +76 -2
- package/lib/ui/session-issue-labels.d.ts +43 -0
- package/lib/ui/session-issue-labels.js +48 -0
- package/lib/ui/sync-settings-view.d.ts +33 -0
- package/lib/ui/sync-settings-view.js +40 -0
- package/lib/ui/test-helpers.js +2 -1
- package/lib/ui/types.d.ts +8 -3
- package/lib/ui/types.js +1 -1
- package/lib/utils/atomic-write.js +6 -5
- package/lib/utils/bundle-scan.d.ts +11 -1
- package/lib/utils/bundle-scan.js +12 -1
- package/lib/utils/env-lock.d.ts +10 -0
- package/lib/utils/env-lock.js +73 -46
- package/lib/utils/json.js +6 -5
- package/lib/utils/messages-utils.d.ts +96 -0
- package/lib/utils/messages-utils.js +216 -0
- package/lib/utils/proxy.js +18 -5
- package/lib/utils/recursive-walk.d.ts +22 -0
- package/lib/utils/recursive-walk.js +75 -1
- package/lib/utils/session-log-repair.js +10 -9
- package/lib/utils/zip.js +27 -26
- package/package.json +12 -2
- package/src/adapters/agent-instructions.ts +1 -1
- package/src/adapters/file-collection.test.ts +1 -0
- package/src/adapters/file-collection.ts +1 -1
- package/src/adapters/files.test.ts +27 -0
- package/src/adapters/index.ts +6 -1
- package/src/adapters/link-report.test.ts +22 -2
- package/src/adapters/link-report.ts +29 -4
- package/src/adapters/messages.ts +61 -0
- package/src/adapters/plugin-data.test.ts +225 -0
- package/src/adapters/plugin-data.ts +351 -0
- package/src/adapters/plugin-files.ts +3 -2
- package/src/adapters/plugins.ts +4 -3
- package/src/adapters/prompts.ts +7 -1
- package/src/adapters/self.test.ts +26 -0
- package/src/adapters/self.ts +10 -3
- package/src/adapters/sessions.ts +4 -2
- package/src/adapters/settings.test.ts +38 -1
- package/src/adapters/test-helpers.ts +15 -0
- package/src/adapters/ui.test.ts +2 -2
- package/src/adapters/ui.ts +27 -26
- package/src/adapters/workspaces.ts +7 -3
- package/src/cli/actions.test.ts +28 -0
- package/src/cli/actions.ts +11 -2
- package/src/cli/help.ts +23 -10
- package/src/cli/index-guards.test.ts +193 -0
- package/src/cli/index.ts +106 -31
- package/src/cli/sessions-inspect.test.ts +15 -1
- package/src/cli/sessions-inspect.ts +3 -1
- package/src/cli/sessions-repair.test.ts +84 -0
- package/src/cli/sessions-repair.ts +33 -7
- package/src/cli/web/console-route-parity.test.ts +115 -0
- package/src/cli/web/page.ts +23 -12
- package/src/cli/web/routes.ts +2 -1
- package/src/cli/web/server.ts +4 -4
- package/src/cli/web/web.test.ts +2 -2
- package/src/client/ConfigManagerSection.tsx +30 -2
- package/src/client/about/AboutPanel.tsx +2 -2
- package/src/client/about/PluginUpdateDialog.tsx +1 -1
- package/src/client/about/ReleaseNotesDialog.tsx +2 -2
- package/src/client/about/about-view.test.ts +13 -3
- package/src/client/about/about-view.ts +10 -6
- package/src/client/about/release-notes-view.test.ts +14 -4
- package/src/client/about/release-notes-view.ts +4 -2
- package/src/client/api.ts +18 -6
- package/src/client/chat/ChatEntryView.tsx +411 -0
- package/src/client/chat/RollbackDiffTab.tsx +108 -0
- package/src/client/chat/RollbackFileIcon.tsx +96 -0
- package/src/client/chat/RollbackReviewDialog.tsx +607 -0
- package/src/client/chat/chat-entry-view.test.ts +1174 -0
- package/src/client/chat/chat-entry-view.ts +758 -0
- package/src/client/chat/chat-locales.ts +152 -0
- package/src/client/chat/chat-node-registration.ts +64 -0
- package/src/client/chat/chat-point-window.ts +183 -0
- package/src/client/chat/chat-slots.ts +107 -0
- package/src/client/chat/rollback-diff-tab.test.ts +105 -0
- package/src/client/chat/rollback-diff-tab.ts +174 -0
- package/src/client/chat/rollback-entry-view.test.ts +1469 -0
- package/src/client/chat/rollback-entry-view.ts +946 -0
- package/src/client/chat/sidebar-slots.ts +59 -0
- package/src/client/common/ConfirmDialog.tsx +7 -3
- package/src/client/common/ContentPicker.tsx +48 -10
- package/src/client/common/DiffTable.tsx +133 -0
- package/src/client/common/ErrorBanner.tsx +4 -4
- package/src/client/common/Icon.tsx +2 -0
- package/src/client/common/Modal.tsx +8 -1
- package/src/client/common/ProgressBar.tsx +8 -2
- package/src/client/common/ReportView.tsx +4 -4
- package/src/client/common/RunsCenter.tsx +1 -0
- package/src/client/common/client-route-parity.test.ts +84 -0
- package/src/client/common/info-hint-guard.test.ts +46 -13
- package/src/client/common/picker-lazy-units.test.ts +42 -0
- package/src/client/common/plan-text-redaction.test.ts +17 -6
- package/src/client/common/progress-view-edge.test.ts +8 -7
- package/src/client/common/progress-view.test.ts +9 -9
- package/src/client/common/progress-view.ts +2 -2
- package/src/client/common/routes.ts +38 -0
- package/src/client/common/section-labels.test.ts +1 -0
- package/src/client/common/section-labels.ts +2 -0
- package/src/client/common/ui.tsx +73 -10
- package/src/client/config-manager.module.css +1109 -31
- package/src/client/css-scale-guard.test.ts +30 -0
- package/src/client/environment/EnvironmentPanel.tsx +11 -4
- package/src/client/environment/maintenance/DiskUsageCard.tsx +23 -2
- package/src/client/export/ExportView.tsx +13 -8
- package/src/client/history/HistoryPanel.tsx +4 -3
- package/src/client/history/history-api.ts +2 -2
- package/src/client/history/history-locales.ts +5 -1
- package/src/client/home/HomePanel.tsx +29 -8
- package/src/client/import/ForeignImportView.tsx +4 -5
- package/src/client/import/ImportWizardView.tsx +5 -3
- package/src/client/import/import-wizard-split.test.ts +3 -1
- package/src/client/import/import-wizard-steps.tsx +9 -9
- package/src/client/index.ts +232 -3
- package/src/client/library/LibraryActions.tsx +32 -7
- package/src/client/library/LibraryPanel.tsx +23 -3
- package/src/client/library/RestorePlanView.tsx +9 -66
- package/src/client/locales.ts +62 -9
- package/src/client/lucide-icons.d.ts +1 -0
- package/src/client/market/MarketPanel.tsx +13 -2
- package/src/client/market/MyConfigsView.tsx +5 -4
- package/src/client/market/MyConfigsWizard.tsx +10 -8
- package/src/client/market/market-api.test.ts +9 -8
- package/src/client/market/market-api.ts +2 -2
- package/src/client/market/market-locales.ts +3 -0
- package/src/client/market/market-view.test.ts +6 -6
- package/src/client/market/market-view.ts +3 -3
- package/src/client/market/my-configs-api.ts +2 -2
- package/src/client/market/my-configs-view.test.ts +23 -22
- package/src/client/market/my-configs-view.ts +8 -8
- package/src/client/recovery/IncompleteCopiesSection.tsx +2 -1
- package/src/client/recovery/RecoveryPanel.tsx +442 -283
- package/src/client/recovery/RollbackCard.tsx +859 -0
- package/src/client/recovery/RollbackDiffPane.tsx +69 -0
- package/src/client/recovery/RollbackFileChanges.tsx +163 -0
- package/src/client/recovery/incident-api.ts +2 -2
- package/src/client/recovery/recovery-api.ts +144 -6
- package/src/client/recovery/recovery-locales.test.ts +299 -0
- package/src/client/recovery/recovery-locales.ts +520 -149
- package/src/client/recovery/recovery-panel-layout.test.ts +132 -2
- package/src/client/recovery/rollback-enabled.ts +41 -0
- package/src/client/recovery/rollback-file-changes.test.ts +114 -0
- package/src/client/recovery/session-dialog-mount.test.ts +54 -0
- package/src/client/run-store.ts +84 -21
- package/src/client/sync/AutosyncCard.tsx +25 -1
- package/src/client/sync/ChannelClearConfirmDialog.tsx +6 -3
- package/src/client/sync/ChannelConfigDialog.tsx +10 -10
- package/src/client/sync/DecryptPasswordCard.tsx +2 -2
- package/src/client/sync/SecurityOptionsCard.tsx +27 -17
- package/src/client/sync/SyncChannelEntryCard.tsx +14 -2
- package/src/client/sync/SyncConfirmView.tsx +46 -8
- package/src/client/sync/SyncLogList.tsx +1 -1
- package/src/client/sync/SyncPanel.tsx +31 -23
- package/src/client/sync/SyncRepositoryPicker.tsx +3 -5
- package/src/client/sync/SyncSectionPickerDialog.tsx +11 -2
- package/src/client/sync/SyncSettingsView.test.ts +20 -1
- package/src/client/sync/UnreadableSnapshotsBanner.tsx +1 -1
- package/src/client/sync/WorkDirCard.tsx +112 -0
- package/src/client/sync/history-model.test.ts +42 -9
- package/src/client/sync/history-model.ts +30 -15
- package/src/client/sync/sync-api.test.ts +57 -33
- package/src/client/sync/sync-api.ts +80 -6
- package/src/client/sync/sync-locales.ts +104 -3
- package/src/client/sync/sync-view-v2.test.ts +65 -13
- package/src/client/sync/sync-view.test.ts +107 -52
- package/src/client/sync/sync-view.ts +124 -41
- package/src/core/analyzer.ts +31 -9
- package/src/core/backup-plan.test.ts +48 -5
- package/src/core/backup-plan.ts +51 -20
- package/src/core/backup-verify.ts +24 -23
- package/src/core/backup.test.ts +76 -0
- package/src/core/backup.ts +105 -29
- package/src/core/boot-rescue.ts +4 -3
- package/src/core/boot-safety.ts +2 -1
- package/src/core/command-rollback.test.ts +536 -0
- package/src/core/command-rollback.ts +406 -0
- package/src/core/consult-source.ts +7 -6
- package/src/core/disk-usage.test.ts +257 -1
- package/src/core/disk-usage.ts +134 -14
- package/src/core/engine-locale.test.ts +593 -0
- package/src/core/engine-locale.ts +175 -0
- package/src/core/exporter.ts +3 -3
- package/src/core/fork-reconcile.test.ts +279 -0
- package/src/core/fork-reconcile.ts +234 -0
- package/src/core/journal.ts +6 -5
- package/src/core/local-plugin-host.ts +3 -2
- package/src/core/local-plugin-pack.ts +11 -6
- package/src/core/messages-core.ts +661 -0
- package/src/core/messages.ts +125 -17
- package/src/core/migration-consult.ts +21 -20
- package/src/core/migration-history.ts +19 -5
- package/src/core/model-tools.ts +28 -27
- package/src/core/phase3-host.ts +21 -5
- package/src/core/phase3-p2b-fingerprint.test.ts +22 -0
- package/src/core/plugin-cli.ts +18 -19
- package/src/core/plugin-data-registry.test.ts +113 -0
- package/src/core/plugin-data-registry.ts +261 -0
- package/src/core/reconcile.test.ts +117 -0
- package/src/core/reconcile.ts +85 -17
- package/src/core/recovery-orchestrator.ts +19 -18
- package/src/core/reinstall.ts +26 -21
- package/src/core/restore.ts +20 -17
- package/src/core/rollback-file-diff.test.ts +159 -0
- package/src/core/rollback-gate.test.ts +66 -0
- package/src/core/rollback-gate.ts +61 -0
- package/src/core/rollback-ignore-rules.ts +126 -0
- package/src/core/rollback-ledger.test.ts +655 -0
- package/src/core/rollback-ledger.ts +1314 -0
- package/src/core/rollback-point.test.ts +2853 -0
- package/src/core/rollback-point.ts +4340 -0
- package/src/core/rollback.ts +5 -3
- package/src/core/run-registry.ts +6 -5
- package/src/core/session-health.ts +15 -0
- package/src/core/session-meta.test.ts +117 -1
- package/src/core/session-meta.ts +149 -2
- package/src/core/session-title.test.ts +127 -0
- package/src/core/session-title.ts +189 -0
- package/src/core/startup-barrier.test.ts +69 -0
- package/src/core/startup-barrier.ts +27 -0
- package/src/core/transaction-coordinator.ts +4 -3
- package/src/core/types.ts +37 -1
- package/src/core/validator.ts +5 -4
- package/src/core/verify-recovery.ts +31 -30
- package/src/foreign/bundle.ts +3 -2
- package/src/foreign/continue.ts +2 -1
- package/src/foreign/crush.ts +2 -1
- package/src/foreign/file-budget.test.ts +2 -0
- package/src/foreign/gemini.ts +2 -1
- package/src/foreign/goose.ts +2 -1
- package/src/foreign/kilocode.ts +2 -1
- package/src/foreign/kimi.ts +2 -1
- package/src/foreign/messages.ts +44 -0
- package/src/foreign/mimocode.ts +2 -1
- package/src/foreign/openclaw.ts +2 -1
- package/src/foreign/opencode.ts +2 -1
- package/src/foreign/pi.ts +2 -1
- package/src/foreign/qoder.ts +2 -1
- package/src/foreign/qwen.ts +2 -1
- package/src/foreign/registry.test.ts +1 -1
- package/src/foreign/registry.ts +8 -7
- package/src/foreign/teleagent.ts +2 -1
- package/src/foreign/workbuddy.ts +2 -1
- package/src/foreign/zcode.ts +2 -1
- package/src/foreign/zed.ts +2 -1
- package/src/index.facade.test.ts +340 -1
- package/src/index.ts +876 -57
- package/src/market/git-file-writer.ts +20 -19
- package/src/market/github-repos.test.ts +23 -0
- package/src/market/github-repos.ts +37 -19
- package/src/market/index-parser.ts +31 -30
- package/src/market/messages-market.ts +325 -0
- package/src/market/my-repo.ts +32 -29
- package/src/market/prepare.ts +18 -15
- package/src/market/security.ts +20 -19
- package/src/market/types.ts +2 -1
- package/src/market/url.ts +2 -1
- package/src/migrations/index.ts +6 -5
- package/src/migrations/messages-migrations.ts +59 -0
- package/src/profiles/dsh-profile-launcher.ts +2 -1
- package/src/profiles/dsh-profile-runtime.ts +2 -1
- package/src/profiles/messages.ts +31 -0
- package/src/routes/backup.ts +18 -2
- package/src/routes/import.ts +3 -3
- package/src/routes/index.ts +1 -1
- package/src/routes/kit.ts +9 -3
- package/src/routes/market.ts +1 -1
- package/src/routes/messages.ts +106 -0
- package/src/routes/prefs.ts +31 -2
- package/src/routes/profiles.ts +12 -6
- package/src/routes/recovery.test.ts +405 -1
- package/src/routes/recovery.ts +477 -94
- package/src/routes/snapshots.ts +51 -4
- package/src/routes/sync.ts +82 -17
- package/src/schema/config.ts +53 -20
- package/src/schema/index.ts +7 -5
- package/src/schema/manifest-schema.test.ts +4 -4
- package/src/schema/manifest.ts +19 -18
- package/src/schema/messages.ts +120 -0
- package/src/schema/registry.test.ts +28 -7
- package/src/schema/section-registry.ts +96 -42
- package/src/schema/types.ts +2 -1
- package/src/schema/versions.ts +6 -5
- package/src/security/encryption.ts +16 -15
- package/src/security/integrity.ts +8 -7
- package/src/security/messages.ts +87 -0
- package/src/security/redaction.test.ts +17 -1
- package/src/security/redaction.ts +13 -0
- package/src/security/secret-scanner.ts +19 -12
- package/src/security/security.test.ts +47 -0
- package/src/security/vault.ts +2 -1
- package/src/security/zip-security.ts +4 -3
- package/src/sync/ancestor.ts +2 -1
- package/src/sync/autosync-config.ts +21 -0
- package/src/sync/autosync-scheduler.test.ts +63 -7
- package/src/sync/autosync-scheduler.ts +118 -78
- package/src/sync/backup-files.ts +3 -2
- package/src/sync/backup-scheduler.ts +6 -5
- package/src/sync/blob-store.ts +2 -1
- package/src/sync/git/git-transport.test.ts +170 -8
- package/src/sync/git/git-transport.ts +176 -2
- package/src/sync/github-auth.ts +10 -9
- package/src/sync/layout.ts +13 -12
- package/src/sync/messages.ts +153 -0
- package/src/sync/review-queue.ts +6 -5
- package/src/sync/s3/messages.ts +16 -0
- package/src/sync/s3/s3-providers.ts +9 -6
- package/src/sync/s3/s3-transport.ts +1 -1
- package/src/sync/s3/sigv4.ts +3 -1
- package/src/sync/snapshot-crypto.ts +6 -5
- package/src/sync/snapshot-json.ts +3 -2
- package/src/sync/sync-credentials.test.ts +119 -0
- package/src/sync/sync-engine-download-tombstone.test.ts +1 -1
- package/src/sync/sync-engine.test.ts +226 -8
- package/src/sync/sync-engine.ts +154 -52
- package/src/sync/sync-history.ts +7 -6
- package/src/sync/sync-selection.test.ts +12 -2
- package/src/sync/sync-selection.ts +18 -5
- package/src/sync/transport.test.ts +1 -0
- package/src/sync/ui-prefs.test.ts +44 -1
- package/src/sync/ui-prefs.ts +39 -0
- package/src/sync/webdav/webdav-transport.ts +4 -1
- package/src/ui/artifact-view.ts +4 -3
- package/src/ui/commands.test.ts +1 -0
- package/src/ui/commands.ts +11 -1
- package/src/ui/diff-view.test.ts +9 -1
- package/src/ui/diff-view.ts +22 -0
- package/src/ui/disk-usage-view.test.ts +113 -6
- package/src/ui/disk-usage-view.ts +63 -7
- package/src/ui/errors.ts +4 -3
- package/src/ui/export-flow.ts +26 -20
- package/src/ui/foreign-view.test.ts +43 -0
- package/src/ui/foreign-view.ts +30 -3
- package/src/ui/i18n.ts +131 -2
- package/src/ui/import-wizard.ts +18 -10
- package/src/ui/progress.ts +2 -2
- package/src/ui/report.ts +7 -6
- package/src/ui/rollback-view.test.ts +836 -0
- package/src/ui/rollback-view.ts +1405 -0
- package/src/ui/selection-model.test.ts +33 -1
- package/src/ui/selection-model.ts +53 -0
- package/src/ui/session-inventory-view.test.ts +109 -0
- package/src/ui/session-inventory-view.ts +138 -3
- package/src/ui/session-issue-labels.test.ts +51 -0
- package/src/ui/session-issue-labels.ts +70 -0
- package/src/ui/sync-settings-view.test.ts +36 -0
- package/src/ui/sync-settings-view.ts +49 -0
- package/src/ui/test-helpers.ts +2 -1
- package/src/ui/types.ts +10 -4
- package/src/utils/atomic-write.ts +6 -5
- package/src/utils/bundle-scan.test.ts +2 -2
- package/src/utils/bundle-scan.ts +12 -1
- package/src/utils/env-lock.ts +83 -49
- package/src/utils/json.ts +7 -5
- package/src/utils/messages-utils.ts +232 -0
- package/src/utils/proxy.test.ts +25 -0
- package/src/utils/proxy.ts +19 -5
- package/src/utils/recursive-walk.test.ts +43 -0
- package/src/utils/recursive-walk.ts +75 -1
- package/src/utils/session-log-repair.ts +10 -9
- package/src/utils/zip.ts +27 -26
- package/lib/core/checkpoint.d.ts +0 -362
- package/lib/core/checkpoint.js +0 -1309
- package/src/core/checkpoint.test.ts +0 -665
- package/src/core/checkpoint.ts +0 -1614
package/lib/market/my-repo.js
CHANGED
|
@@ -34,13 +34,16 @@ import { MARKET_INDEX_SCHEMA_VERSION } from './types.js';
|
|
|
34
34
|
import { stringifyJsonSafe } from '../utils/json.js';
|
|
35
35
|
import { sha256Hex } from '../utils/hashing.js';
|
|
36
36
|
import { redact } from '../security/redaction.js';
|
|
37
|
+
import { marketMsg } from './messages-market.js';
|
|
37
38
|
/* ---------------------------------------------------------------- 常量(固定规则,无配置入口) */
|
|
38
39
|
/** 官方收录目标仓库原样复用 t1 常量(github-repos.ts)
|
|
39
40
|
* —— 与内置市场同一仓库,界面不提供任何修改入口(设计 §2.4 产品决策)。 */
|
|
40
41
|
/** 用户公开仓库固定名:<login>/dsh-configs(系统按规则生成,不可配置) */
|
|
41
42
|
export const USER_CONFIGS_REPO = 'dsh-configs';
|
|
42
|
-
/** 用户仓库 index.json
|
|
43
|
-
export
|
|
43
|
+
/** 用户仓库 index.json 的名称/描述(展示用;按当前界面语言在**取用时**解析,不冻结在模块加载期) */
|
|
44
|
+
export function userIndexName() {
|
|
45
|
+
return marketMsg('market.myRepo.indexName');
|
|
46
|
+
}
|
|
44
47
|
/** PR 分支固定名模式:dsh-market-sync/<itemId>(收录) */
|
|
45
48
|
export const PR_BRANCH_PREFIX = 'dsh-market-sync';
|
|
46
49
|
/** 下架 PR 分支固定名模式:dsh-market-delist/<itemId>(删除已收录条目;独立分支避免与收录分支混淆,
|
|
@@ -101,7 +104,7 @@ export function bumpVersion(version) {
|
|
|
101
104
|
}
|
|
102
105
|
/** 空用户索引(首次上传时创建) */
|
|
103
106
|
function emptyIndex() {
|
|
104
|
-
return { schemaVersion: MARKET_INDEX_SCHEMA_VERSION, name:
|
|
107
|
+
return { schemaVersion: MARKET_INDEX_SCHEMA_VERSION, name: userIndexName(), items: [] };
|
|
105
108
|
}
|
|
106
109
|
/** upsert 条目:同 id 覆盖(保持原位置),否则追加 */
|
|
107
110
|
function upsertIndexItem(index, item) {
|
|
@@ -168,10 +171,10 @@ export class MyRepoService {
|
|
|
168
171
|
loginWriteQueues = new Map();
|
|
169
172
|
constructor(options) {
|
|
170
173
|
if (options.prepare === null || typeof options.prepare !== 'function') {
|
|
171
|
-
throw new MyRepoError('
|
|
174
|
+
throw new MyRepoError(marketMsg('market.myRepo.prepareRequired'));
|
|
172
175
|
}
|
|
173
176
|
if (options.rest === null || typeof options.rest !== 'object') {
|
|
174
|
-
throw new MyRepoError('
|
|
177
|
+
throw new MyRepoError(marketMsg('market.myRepo.restRequired'));
|
|
175
178
|
}
|
|
176
179
|
this.prepare = options.prepare;
|
|
177
180
|
this.rest = options.rest;
|
|
@@ -197,7 +200,7 @@ export class MyRepoService {
|
|
|
197
200
|
return []; // 从未上传
|
|
198
201
|
const parsed = parseMarketIndex(indexText);
|
|
199
202
|
if (!parsed.ok || parsed.index === null) {
|
|
200
|
-
throw new MyRepoError('
|
|
203
|
+
throw new MyRepoError(marketMsg('market.myRepo.userIndexUnparsable'), 'index_invalid');
|
|
201
204
|
}
|
|
202
205
|
// 收录状态:官方 index 含该 id → listed;否则按设计 §4.5 以 head=<login>:<branch>
|
|
203
206
|
// 过滤 open PR(避免跨用户同名分支误报)→ pr-pending;否则 not-listed
|
|
@@ -284,7 +287,7 @@ export class MyRepoService {
|
|
|
284
287
|
const { index } = await this.readUserIndex(login);
|
|
285
288
|
const item = index.items.find((it) => it.id === itemId);
|
|
286
289
|
if (item === undefined) {
|
|
287
|
-
throw new MyRepoError(
|
|
290
|
+
throw new MyRepoError(marketMsg('market.myRepo.itemNotFound', { itemId }), 'item_not_found');
|
|
288
291
|
}
|
|
289
292
|
const repoUrl = userConfigsRepoUrl(login);
|
|
290
293
|
this.launchListing({
|
|
@@ -305,7 +308,7 @@ export class MyRepoService {
|
|
|
305
308
|
if (job !== undefined && job.status !== 'pending')
|
|
306
309
|
return this.toListingStatus(job);
|
|
307
310
|
if (Date.now() >= deadline) {
|
|
308
|
-
throw new MyRepoError(
|
|
311
|
+
throw new MyRepoError(marketMsg('market.myRepo.listingTimeout', { itemId }), 'internal');
|
|
309
312
|
}
|
|
310
313
|
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
311
314
|
}
|
|
@@ -329,7 +332,7 @@ export class MyRepoService {
|
|
|
329
332
|
const { index } = await this.readUserIndex(login);
|
|
330
333
|
const item = index.items.find((it) => it.id === itemId);
|
|
331
334
|
if (item === undefined) {
|
|
332
|
-
throw new MyRepoError(
|
|
335
|
+
throw new MyRepoError(marketMsg('market.myRepo.itemToDeleteNotFound', { itemId }), 'item_not_found');
|
|
333
336
|
}
|
|
334
337
|
// ② 移除条目 + 删除 items/<itemId>/ 目录 → 写回 index.json(per-login 串行)
|
|
335
338
|
const updatedIndex = removeIndexItem(index, itemId);
|
|
@@ -367,7 +370,7 @@ export class MyRepoService {
|
|
|
367
370
|
delisted: true,
|
|
368
371
|
prNumber: null,
|
|
369
372
|
prUrl: null,
|
|
370
|
-
warnings: ['
|
|
373
|
+
warnings: [marketMsg('market.myRepo.delistPrSubmitted')],
|
|
371
374
|
};
|
|
372
375
|
}
|
|
373
376
|
// 未收录 → 关闭待审核的收录 PR(如有)
|
|
@@ -381,7 +384,7 @@ export class MyRepoService {
|
|
|
381
384
|
delisted: false,
|
|
382
385
|
prNumber: pr.number,
|
|
383
386
|
prUrl: pr.htmlUrl,
|
|
384
|
-
warnings: ['
|
|
387
|
+
warnings: [marketMsg('market.myRepo.pendingPrClosed')],
|
|
385
388
|
};
|
|
386
389
|
}
|
|
387
390
|
return { ok: true, itemId, delisted: false, prNumber: null, prUrl: null, warnings: [] };
|
|
@@ -429,12 +432,12 @@ export class MyRepoService {
|
|
|
429
432
|
}
|
|
430
433
|
else if (explicitId !== null) {
|
|
431
434
|
// 明确要求更新一个不存在的 id → 报错(避免静默新建导致用户以为更新成功)
|
|
432
|
-
throw new MyRepoError(
|
|
435
|
+
throw new MyRepoError(marketMsg('market.myRepo.itemToUpdateNotFound', { itemId: explicitId }), 'item_not_found');
|
|
433
436
|
}
|
|
434
437
|
else {
|
|
435
438
|
itemId = uniqueItemId(baseId, ids); // 名称变化 → 作为新条目
|
|
436
439
|
version = '1.0.0';
|
|
437
|
-
warnings.push('
|
|
440
|
+
warnings.push(marketMsg('market.myRepo.nameChangedNewItem'));
|
|
438
441
|
}
|
|
439
442
|
}
|
|
440
443
|
else {
|
|
@@ -509,7 +512,7 @@ export class MyRepoService {
|
|
|
509
512
|
return { index: emptyIndex(), exists: false };
|
|
510
513
|
const parsed = parseMarketIndex(text);
|
|
511
514
|
if (!parsed.ok || parsed.index === null) {
|
|
512
|
-
throw new MyRepoError('
|
|
515
|
+
throw new MyRepoError(marketMsg('market.myRepo.userIndexUnparsableAborted'), 'index_invalid');
|
|
513
516
|
}
|
|
514
517
|
return { index: parsed.index, exists: true };
|
|
515
518
|
}
|
|
@@ -517,11 +520,11 @@ export class MyRepoService {
|
|
|
517
520
|
async readOfficialIndex() {
|
|
518
521
|
const text = await this.rest.readFile(MARKET_UPSTREAM_OWNER, MARKET_UPSTREAM_REPO, 'index.json');
|
|
519
522
|
if (text === null) {
|
|
520
|
-
throw new MyRepoError('
|
|
523
|
+
throw new MyRepoError(marketMsg('market.myRepo.officialIndexMissing'), 'official_index_missing');
|
|
521
524
|
}
|
|
522
525
|
const parsed = parseMarketIndex(text);
|
|
523
526
|
if (!parsed.ok || parsed.index === null) {
|
|
524
|
-
throw new MyRepoError('
|
|
527
|
+
throw new MyRepoError(marketMsg('market.myRepo.officialIndexUnparsable'), 'official_index_invalid');
|
|
525
528
|
}
|
|
526
529
|
return { index: parsed.index };
|
|
527
530
|
}
|
|
@@ -529,7 +532,7 @@ export class MyRepoService {
|
|
|
529
532
|
parsePreparedManifest(manifestText) {
|
|
530
533
|
const parsed = parseMarketItemManifest(manifestText);
|
|
531
534
|
if (!parsed.ok || parsed.manifest === null) {
|
|
532
|
-
throw new MyRepoError('
|
|
535
|
+
throw new MyRepoError(marketMsg('market.myRepo.preparedManifestInvalid'), 'internal');
|
|
533
536
|
}
|
|
534
537
|
return parsed.manifest;
|
|
535
538
|
}
|
|
@@ -546,7 +549,7 @@ export class MyRepoService {
|
|
|
546
549
|
fork: false,
|
|
547
550
|
};
|
|
548
551
|
}
|
|
549
|
-
return this.rest.createPublicRepo(USER_CONFIGS_REPO, '
|
|
552
|
+
return this.rest.createPublicRepo(USER_CONFIGS_REPO, marketMsg('market.myRepo.repoDescription'));
|
|
550
553
|
}
|
|
551
554
|
/** ⑨ ensure PR:open PR(head=<login>:<branch>)存在 → 复用;否则 openPullRequest(base=main)。
|
|
552
555
|
* action='delist' 时标题/正文为下架语义(分支用 delistBranchFor,head 过滤天然隔离)。 */
|
|
@@ -559,26 +562,26 @@ export class MyRepoService {
|
|
|
559
562
|
return this.rest.openPullRequest({
|
|
560
563
|
head,
|
|
561
564
|
base: 'main',
|
|
562
|
-
title:
|
|
565
|
+
title: marketMsg('market.myRepo.delistPrTitle', { itemId }),
|
|
563
566
|
body: [
|
|
564
|
-
|
|
567
|
+
marketMsg('market.myRepo.delistPrIntro'),
|
|
565
568
|
'',
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
+
marketMsg('market.myRepo.delistPrItemLine', { itemId }),
|
|
570
|
+
marketMsg('market.myRepo.delistPrAction'),
|
|
571
|
+
marketMsg('market.myRepo.delistPrContent'),
|
|
569
572
|
].join('\n'),
|
|
570
573
|
});
|
|
571
574
|
}
|
|
572
575
|
return this.rest.openPullRequest({
|
|
573
576
|
head,
|
|
574
577
|
base: 'main',
|
|
575
|
-
title:
|
|
578
|
+
title: marketMsg('market.myRepo.listPrTitle', { itemId, version }),
|
|
576
579
|
body: [
|
|
577
|
-
|
|
580
|
+
marketMsg('market.myRepo.listPrIntro'),
|
|
578
581
|
'',
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
+
marketMsg('market.myRepo.listPrItemLine', { itemId, version }),
|
|
583
|
+
marketMsg('market.myRepo.listPrHosting'),
|
|
584
|
+
marketMsg('market.myRepo.listPrValidation'),
|
|
582
585
|
].join('\n'),
|
|
583
586
|
});
|
|
584
587
|
}
|
|
@@ -626,7 +629,7 @@ export class MyRepoService {
|
|
|
626
629
|
const { index } = await this.readUserIndex(job.login);
|
|
627
630
|
const item = index.items.find((it) => it.id === job.itemId);
|
|
628
631
|
if (item === undefined) {
|
|
629
|
-
throw new MyRepoError(
|
|
632
|
+
throw new MyRepoError(marketMsg('market.myRepo.itemGoneFromUserRepo', { itemId: job.itemId }), 'item_not_found');
|
|
630
633
|
}
|
|
631
634
|
updatedOfficial = upsertIndexItem(officialIndex.index, { ...item, repo: job.repoUrl });
|
|
632
635
|
commitMsg = `market: ${item.version !== undefined && item.version !== '' ? 'update' : 'add'} ${job.itemId} v${job.version}`;
|
package/lib/market/prepare.js
CHANGED
|
@@ -19,6 +19,7 @@ import { sectionMeta } from '../schema/section-registry.js';
|
|
|
19
19
|
import { scanAndRedact, scanText } from '../security/secret-scanner.js';
|
|
20
20
|
import { validateMarketRepoUrl } from './url.js';
|
|
21
21
|
import { assertSafeItemId, BANNED_MARKET_SECTIONS, MARKET_ITEM_SCHEMA_VERSION, MAX_MARKET_ZIP_BYTES, } from './types.js';
|
|
22
|
+
import { marketMsg } from './messages-market.js';
|
|
22
23
|
export class MarketPrepareError extends Error {
|
|
23
24
|
constructor(message) {
|
|
24
25
|
super(message);
|
|
@@ -38,19 +39,19 @@ export function prepareMarketItem(input) {
|
|
|
38
39
|
// 1. id / name 基础校验
|
|
39
40
|
assertSafeItemId(input.itemId);
|
|
40
41
|
if (typeof input.name !== 'string' || input.name.trim() === '') {
|
|
41
|
-
throw new MarketPrepareError('
|
|
42
|
+
throw new MarketPrepareError(marketMsg('market.prepare.nameRequired'));
|
|
42
43
|
}
|
|
43
44
|
// 2. repoUrl 可选校验(强制 http(s),拒绝 userinfo / git@/ssh;非法即拒绝发布)
|
|
44
45
|
const repoUrl = typeof input.repoUrl === 'string' && input.repoUrl.trim() !== '' ? input.repoUrl.trim() : undefined;
|
|
45
46
|
if (repoUrl !== undefined) {
|
|
46
47
|
const err = validateMarketRepoUrl(repoUrl);
|
|
47
48
|
if (err !== null)
|
|
48
|
-
throw new MarketPrepareError(
|
|
49
|
+
throw new MarketPrepareError(marketMsg('market.prepare.repoUrlInvalid', { reason: err }));
|
|
49
50
|
}
|
|
50
51
|
// 3. zip 体积上限(zip bomb 首道闸,与 security.ts 一致)
|
|
51
52
|
const buf = Buffer.isBuffer(input.zipBytes) ? input.zipBytes : Buffer.from(input.zipBytes);
|
|
52
53
|
if (buf.length > MAX_MARKET_ZIP_BYTES) {
|
|
53
|
-
throw new MarketPrepareError(
|
|
54
|
+
throw new MarketPrepareError(marketMsg('market.prepare.zipTooLarge', { size: buf.length, max: MAX_MARKET_ZIP_BYTES }));
|
|
54
55
|
}
|
|
55
56
|
// 4. 加固解包(Zip Slip / 绝对路径 / 恶意条目;异常 → 拒绝)
|
|
56
57
|
let archive;
|
|
@@ -58,39 +59,39 @@ export function prepareMarketItem(input) {
|
|
|
58
59
|
archive = parseZipHardened(buf);
|
|
59
60
|
}
|
|
60
61
|
catch (err) {
|
|
61
|
-
throw new MarketPrepareError(
|
|
62
|
+
throw new MarketPrepareError(marketMsg('market.prepare.zipParseFailed', { reason: err instanceof Error ? err.message : String(err) }));
|
|
62
63
|
}
|
|
63
64
|
// 5. 内部 manifest + secrets 硬约束
|
|
64
65
|
if (!archive.has(MANIFEST_FILE)) {
|
|
65
|
-
throw new MarketPrepareError('
|
|
66
|
+
throw new MarketPrepareError(marketMsg('market.prepare.zipMissingManifest'));
|
|
66
67
|
}
|
|
67
68
|
let internalManifest;
|
|
68
69
|
try {
|
|
69
70
|
internalManifest = parseManifest(archive.readEntryText(MANIFEST_FILE));
|
|
70
71
|
}
|
|
71
72
|
catch (err) {
|
|
72
|
-
throw new MarketPrepareError(
|
|
73
|
+
throw new MarketPrepareError(marketMsg('market.prepare.zipManifestInvalid', { reason: err instanceof Error ? err.message : String(err) }));
|
|
73
74
|
}
|
|
74
75
|
if (internalManifest.security.containsSecrets) {
|
|
75
|
-
throw new MarketPrepareError('
|
|
76
|
+
throw new MarketPrepareError(marketMsg('market.prepare.zipContainsSecrets'));
|
|
76
77
|
}
|
|
77
78
|
// 6. sections = 内部 manifest 启用的分区(空 → 无可发布内容)
|
|
78
79
|
const sections = Object.entries(internalManifest.sections)
|
|
79
80
|
.filter(([, on]) => on)
|
|
80
81
|
.map(([id]) => id);
|
|
81
82
|
if (sections.length === 0) {
|
|
82
|
-
throw new MarketPrepareError('
|
|
83
|
+
throw new MarketPrepareError(marketMsg('market.prepare.zipNoSections'));
|
|
83
84
|
}
|
|
84
85
|
// 6b. 市场条目禁止分区(BANNED_MARKET_SECTIONS:sessions 历史会话 / pluginFiles 任意文件 / self 本地环境)
|
|
85
86
|
const bannedHit = sections.filter((s) => BANNED_MARKET_SECTIONS.includes(s));
|
|
86
87
|
if (bannedHit.length > 0) {
|
|
87
|
-
throw new MarketPrepareError(
|
|
88
|
+
throw new MarketPrepareError(marketMsg('market.prepare.bannedSections', { sections: bannedHit.join(', ') }));
|
|
88
89
|
}
|
|
89
90
|
// 6b2. 分享模式额外排除:deviceSpecific/platformSpecific 分区(比 BANNED 更严,与 BANNED 同语义——直接拒绝)
|
|
90
91
|
if (isShare) {
|
|
91
92
|
const nonPortable = sections.filter((s) => sectionMeta(s).portability !== 'portable');
|
|
92
93
|
if (nonPortable.length > 0) {
|
|
93
|
-
throw new MarketPrepareError(
|
|
94
|
+
throw new MarketPrepareError(marketMsg('market.prepare.shareNonPortableSections', { sections: nonPortable.join(', ') }));
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
// 6b3. T1:plugins 分区禁止携带本地插件 tarball(localTarballs)。
|
|
@@ -103,12 +104,12 @@ export function prepareMarketItem(input) {
|
|
|
103
104
|
: null;
|
|
104
105
|
const tb = pluginsJson?.localTarballs;
|
|
105
106
|
if (Array.isArray(tb) && tb.length > 0) {
|
|
106
|
-
throw new MarketPrepareError(
|
|
107
|
+
throw new MarketPrepareError(marketMsg('market.prepare.localTarballsRejected', { count: tb.length }));
|
|
107
108
|
}
|
|
108
109
|
// issue #35:patchFiles 同类拒绝(patch 在安装时改写依赖代码,同样不可经公开仓库审阅)
|
|
109
110
|
const pfs = pluginsJson?.patchFiles;
|
|
110
111
|
if (Array.isArray(pfs) && pfs.length > 0) {
|
|
111
|
-
throw new MarketPrepareError(
|
|
112
|
+
throw new MarketPrepareError(marketMsg('market.prepare.patchFilesRejected', { count: pfs.length }));
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
// 6c. 内容级秘密扫描(纵深防御,不依赖导出 containsSecrets 标记):
|
|
@@ -152,7 +153,9 @@ export function prepareMarketItem(input) {
|
|
|
152
153
|
if (scanHits.length > 0) {
|
|
153
154
|
const limit = isShare ? 10 : 3;
|
|
154
155
|
const suffix = scanHits.length > limit ? '…' : '';
|
|
155
|
-
throw new MarketPrepareError(
|
|
156
|
+
throw new MarketPrepareError(marketMsg('market.prepare.sensitiveContent', {
|
|
157
|
+
hits: scanHits.slice(0, limit).join(marketMsg('market.prepare.scanHitSeparator')) + suffix,
|
|
158
|
+
}));
|
|
156
159
|
}
|
|
157
160
|
// 7. SHA-256 + 生成 L2 manifest
|
|
158
161
|
const sha256 = sha256Hex(buf);
|
|
@@ -178,8 +181,8 @@ export function prepareMarketItem(input) {
|
|
|
178
181
|
sha256,
|
|
179
182
|
sections,
|
|
180
183
|
warnings: [
|
|
181
|
-
'
|
|
182
|
-
'
|
|
184
|
+
marketMsg('market.prepare.warningPublicMarket'),
|
|
185
|
+
marketMsg('market.prepare.warningUntrustedInput'),
|
|
183
186
|
],
|
|
184
187
|
};
|
|
185
188
|
}
|
package/lib/market/security.js
CHANGED
|
@@ -16,6 +16,7 @@ import { parseManifest, CHECKSUMS_FILE, MANIFEST_FILE } from '../schema/manifest
|
|
|
16
16
|
import { validateSectionData, SECTION_JSON_PATHS, SECTION_FILE_PREFIXES, isFileSection } from '../schema/config.js';
|
|
17
17
|
import { parseMarketItemManifest } from './index-parser.js';
|
|
18
18
|
import { MAX_MARKET_ZIP_BYTES, BANNED_MARKET_SECTIONS } from './types.js';
|
|
19
|
+
import { marketMsg } from './messages-market.js';
|
|
19
20
|
// 限额唯一来源 = utils/zip.ts 的 DEFAULT_ZIP_SAFETY_LIMITS(此处曾复制一份字面量,纯冗余):
|
|
20
21
|
// 不传 defaultLimits → 与默认解析路径、读侧上限逐项一致。
|
|
21
22
|
const parseZipHardened = createHardenedZipParser();
|
|
@@ -36,17 +37,17 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
36
37
|
}
|
|
37
38
|
const manifest = parsedManifest.manifest;
|
|
38
39
|
if (manifest.id !== itemId) {
|
|
39
|
-
return { status: 'invalid', errors: [
|
|
40
|
+
return { status: 'invalid', errors: [marketMsg('market.security.manifestIdMismatch', { manifestId: manifest.id, itemId })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
|
|
40
41
|
}
|
|
41
42
|
const buf = Buffer.isBuffer(zipBytes) ? zipBytes : Buffer.from(zipBytes);
|
|
42
43
|
// 2. 字节体积上限(zip bomb 首道闸)
|
|
43
44
|
if (buf.length > MAX_MARKET_ZIP_BYTES) {
|
|
44
|
-
return { status: 'invalid', errors: [
|
|
45
|
+
return { status: 'invalid', errors: [marketMsg('market.security.zipTooLarge', { size: buf.length, max: MAX_MARKET_ZIP_BYTES })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
|
|
45
46
|
}
|
|
46
47
|
// 3. L2↔L3 一致性:清单声明的 config.zip SHA-256 与实际一致
|
|
47
48
|
const actualSha = sha256Hex(buf);
|
|
48
49
|
if (manifest.checksums.zip !== actualSha) {
|
|
49
|
-
return { status: 'invalid', errors: [
|
|
50
|
+
return { status: 'invalid', errors: [marketMsg('market.security.zipShaMismatch')], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
|
|
50
51
|
}
|
|
51
52
|
// 4. Zip 加固解包(Zip Slip / 绝对路径 / 恶意条目 / 重复 / symlink;异常 → 拒绝整包)
|
|
52
53
|
let archive;
|
|
@@ -54,21 +55,21 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
54
55
|
archive = parseZipHardened(buf);
|
|
55
56
|
}
|
|
56
57
|
catch (err) {
|
|
57
|
-
return { status: 'invalid', errors: [
|
|
58
|
+
return { status: 'invalid', errors: [marketMsg('market.security.zipParseFailed', { reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
|
|
58
59
|
}
|
|
59
60
|
// 5. 内部 manifest + 分区校验
|
|
60
61
|
if (!archive.has(MANIFEST_FILE)) {
|
|
61
|
-
return { status: 'invalid', errors: ['
|
|
62
|
+
return { status: 'invalid', errors: [marketMsg('market.security.zipMissingManifest')], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
|
|
62
63
|
}
|
|
63
64
|
let internalManifest;
|
|
64
65
|
try {
|
|
65
66
|
internalManifest = parseManifest(archive.readEntryText(MANIFEST_FILE));
|
|
66
67
|
}
|
|
67
68
|
catch (err) {
|
|
68
|
-
return { status: 'invalid', errors: [
|
|
69
|
+
return { status: 'invalid', errors: [marketMsg('market.security.zipManifestInvalid', { reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest: null, sections: [], checksumsOk: false };
|
|
69
70
|
}
|
|
70
71
|
if (internalManifest.security.containsSecrets) {
|
|
71
|
-
return { status: 'invalid', errors: ['
|
|
72
|
+
return { status: 'invalid', errors: [marketMsg('market.security.zipContainsSecrets')], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
|
|
72
73
|
}
|
|
73
74
|
// 6. 内部 checksum 校验(integrity/checksums.json ↔ 实际条目)
|
|
74
75
|
let checksumsOk = false;
|
|
@@ -89,11 +90,11 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
89
90
|
const result = verifyChecksums(entries, table);
|
|
90
91
|
checksumsOk = result.ok;
|
|
91
92
|
if (!checksumsOk) {
|
|
92
|
-
return { status: 'invalid', errors: [
|
|
93
|
+
return { status: 'invalid', errors: [marketMsg('market.security.checksumMismatch', { entries: [...result.mismatches, ...result.missing].join(', ') })], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
catch (err) {
|
|
96
|
-
return { status: 'invalid', errors: [
|
|
97
|
+
return { status: 'invalid', errors: [marketMsg('market.security.checksumsParseFailed', { reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest, sections: [], checksumsOk: false };
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
100
|
// 7. 内部分区数据校验(JSON 分区逐一 validateSectionData)
|
|
@@ -103,7 +104,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
103
104
|
// 市场条目禁止分区(BANNED_MARKET_SECTIONS:sessions 历史会话 / pluginFiles 任意文件 / self 本地环境):
|
|
104
105
|
// 与 secrets 同级硬约束(产品决策 2026-08-19,guide docs/design/2026-08-19-market-repo-setup-guide.md)。
|
|
105
106
|
if (BANNED_MARKET_SECTIONS.includes(sectionId)) {
|
|
106
|
-
return { status: 'invalid', errors: [
|
|
107
|
+
return { status: 'invalid', errors: [marketMsg('market.security.bannedSection', { section: sectionId })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
|
|
107
108
|
}
|
|
108
109
|
if (isFileSection(sectionId)) {
|
|
109
110
|
// 文件类分区:以文件形式进入 ZIP,结构校验由 security/safeExtract 在导入期做。
|
|
@@ -112,7 +113,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
112
113
|
// 与 sync 通道对空分区的语义一致(空分区参与 manifest 标记但不产生任何文件)。
|
|
113
114
|
// 仅加 warning 提示,不拒绝;JSON 分区仍严格要求文件存在(有内容才可能 enabled)。
|
|
114
115
|
if (!archive.names().some((n) => n.startsWith(SECTION_FILE_PREFIXES[sectionId]))) {
|
|
115
|
-
warnings.push(
|
|
116
|
+
warnings.push(marketMsg('market.security.emptyFileSection', { section: sectionId }));
|
|
116
117
|
}
|
|
117
118
|
continue;
|
|
118
119
|
}
|
|
@@ -120,19 +121,19 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
120
121
|
if (jsonPath === undefined)
|
|
121
122
|
continue;
|
|
122
123
|
if (!archive.has(jsonPath)) {
|
|
123
|
-
return { status: 'invalid', errors: [
|
|
124
|
+
return { status: 'invalid', errors: [marketMsg('market.security.missingSectionFile', { path: jsonPath, section: sectionId })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
|
|
124
125
|
}
|
|
125
126
|
let data;
|
|
126
127
|
try {
|
|
127
128
|
data = archive.readEntryJson(jsonPath);
|
|
128
129
|
}
|
|
129
130
|
catch (err) {
|
|
130
|
-
return { status: 'invalid', errors: [
|
|
131
|
+
return { status: 'invalid', errors: [marketMsg('market.security.sectionParseFailed', { section: sectionId, reason: err instanceof Error ? err.message : String(err) })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
|
|
131
132
|
}
|
|
132
133
|
const issues = validateSectionData(sectionId, data);
|
|
133
134
|
const hardErrors = issues.filter((i) => i.severity === 'error');
|
|
134
135
|
if (hardErrors.length > 0) {
|
|
135
|
-
return { status: 'invalid', errors: [
|
|
136
|
+
return { status: 'invalid', errors: [marketMsg('market.security.sectionInvalid', { section: sectionId, errors: hardErrors.map((e) => e.message).join('; ') })], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
|
|
136
137
|
}
|
|
137
138
|
// T1 供应链约束:plugins 分区禁止携带本地插件 tarball(localTarballs)。
|
|
138
139
|
//
|
|
@@ -146,7 +147,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
146
147
|
if (Array.isArray(tarballs) && tarballs.length > 0) {
|
|
147
148
|
return {
|
|
148
149
|
status: 'invalid',
|
|
149
|
-
errors: [
|
|
150
|
+
errors: [marketMsg('market.security.localTarballsRejected', { count: tarballs.length })],
|
|
150
151
|
warnings, manifest, internalManifest, sections: [], checksumsOk: true,
|
|
151
152
|
};
|
|
152
153
|
}
|
|
@@ -156,7 +157,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
156
157
|
if (Array.isArray(patchFiles) && patchFiles.length > 0) {
|
|
157
158
|
return {
|
|
158
159
|
status: 'invalid',
|
|
159
|
-
errors: [
|
|
160
|
+
errors: [marketMsg('market.security.patchFilesRejected', { count: patchFiles.length })],
|
|
160
161
|
warnings, manifest, internalManifest, sections: [], checksumsOk: true,
|
|
161
162
|
};
|
|
162
163
|
}
|
|
@@ -169,7 +170,7 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
169
170
|
.map(([id]) => id);
|
|
170
171
|
const intersection = l3Sections.filter((id) => l2Sections.has(id));
|
|
171
172
|
if (intersection.length === 0) {
|
|
172
|
-
return { status: 'invalid', errors: ['
|
|
173
|
+
return { status: 'invalid', errors: [marketMsg('market.security.sectionsNoIntersection')], warnings, manifest, internalManifest, sections: [], checksumsOk: true };
|
|
173
174
|
}
|
|
174
175
|
return { status: 'valid', errors: [], warnings, manifest, internalManifest, sections: intersection, checksumsOk };
|
|
175
176
|
}
|
|
@@ -179,8 +180,8 @@ export function validateMarketItem(itemId, manifestRaw, zipBytes) {
|
|
|
179
180
|
*/
|
|
180
181
|
export function generateSupplyChainWarnings(itemId) {
|
|
181
182
|
return [
|
|
182
|
-
|
|
183
|
-
'
|
|
183
|
+
marketMsg('market.security.warningFromPublicMarket', { itemId }),
|
|
184
|
+
marketMsg('market.security.warningUntrustedContent'),
|
|
184
185
|
];
|
|
185
186
|
}
|
|
186
187
|
//# sourceMappingURL=security.js.map
|
package/lib/market/types.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { marketMsg } from './messages-market.js';
|
|
1
2
|
/** index.json schema 版本(本版恒 1) */
|
|
2
3
|
export const MARKET_INDEX_SCHEMA_VERSION = 1;
|
|
3
4
|
/** items/<id>/manifest.json schema 版本(本版恒 1) */
|
|
@@ -22,7 +23,7 @@ export const SAFE_ITEM_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
|
22
23
|
/** 校验 itemId 是否合法;不合法抛错(防 items/<id>/ 越界)。 */
|
|
23
24
|
export function assertSafeItemId(id) {
|
|
24
25
|
if (typeof id !== 'string' || !SAFE_ITEM_ID_RE.test(id)) {
|
|
25
|
-
throw new Error(
|
|
26
|
+
throw new Error(marketMsg('market.types.itemIdInvalid', { id: JSON.stringify(id) }));
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
//# sourceMappingURL=types.js.map
|
package/lib/market/url.js
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* 复用 sync 的 validateRepoUrl(拒绝 userinfo / 空白),叠加 scheme 强制。
|
|
8
8
|
*/
|
|
9
9
|
import { validateRepoUrl } from '../sync/sync-config.js';
|
|
10
|
+
import { marketMsg } from './messages-market.js';
|
|
10
11
|
/** 校验市场仓库地址;合法返回 null,非法返回错误消息。仅接受 http(s),拒绝 git@/ssh/空白/含 userinfo。 */
|
|
11
12
|
export function validateMarketRepoUrl(repoUrl) {
|
|
12
13
|
const err = validateRepoUrl(repoUrl);
|
|
13
14
|
if (err !== null)
|
|
14
15
|
return err;
|
|
15
16
|
if (!/^https?:\/\//i.test(repoUrl.trim())) {
|
|
16
|
-
return '
|
|
17
|
+
return marketMsg('market.url.schemeUnsupported');
|
|
17
18
|
}
|
|
18
19
|
return null;
|
|
19
20
|
}
|
package/lib/migrations/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { CURRENT_SCHEMA_VERSION, MIN_SUPPORTED_SCHEMA_VERSION, UnsupportedSchemaError } from '../schema/versions.js';
|
|
8
8
|
import { V1_TO_V2 } from './v1-to-v2.js';
|
|
9
|
+
import { migMsg } from './messages-migrations.js';
|
|
9
10
|
/**
|
|
10
11
|
* 迁移注册表:按 from 升序的链式步骤。
|
|
11
12
|
* 当前只有 v1→v2 占位(CURRENT=1 时不会触发,schema v2 发布后生效)。
|
|
@@ -15,7 +16,7 @@ export const MIGRATIONS = [V1_TO_V2];
|
|
|
15
16
|
export function registerMigration(step) {
|
|
16
17
|
const dup = MIGRATIONS.find((s) => s.from === step.from || s.to === step.to);
|
|
17
18
|
if (dup)
|
|
18
|
-
throw new Error(
|
|
19
|
+
throw new Error(migMsg('migrations.stepConflict', { stepFrom: step.from, stepTo: step.to, dupFrom: dup.from, dupTo: dup.to }));
|
|
19
20
|
MIGRATIONS.push(step);
|
|
20
21
|
MIGRATIONS.sort((a, b) => a.from - b.from);
|
|
21
22
|
}
|
|
@@ -31,10 +32,10 @@ export function migrateToCurrent(doc, fromVersion, targetVersion = CURRENT_SCHEM
|
|
|
31
32
|
return { doc, migratedFrom: fromVersion, applied: [] };
|
|
32
33
|
}
|
|
33
34
|
if (fromVersion > targetVersion) {
|
|
34
|
-
throw new UnsupportedSchemaError(fromVersion,
|
|
35
|
+
throw new UnsupportedSchemaError(fromVersion, migMsg('migrations.schemaTooNew', { from: fromVersion, target: targetVersion }));
|
|
35
36
|
}
|
|
36
37
|
if (fromVersion < MIN_SUPPORTED_SCHEMA_VERSION) {
|
|
37
|
-
throw new UnsupportedSchemaError(fromVersion,
|
|
38
|
+
throw new UnsupportedSchemaError(fromVersion, migMsg('migrations.schemaTooOld', { from: fromVersion, min: MIN_SUPPORTED_SCHEMA_VERSION }));
|
|
38
39
|
}
|
|
39
40
|
let current = doc;
|
|
40
41
|
let version = fromVersion;
|
|
@@ -42,10 +43,10 @@ export function migrateToCurrent(doc, fromVersion, targetVersion = CURRENT_SCHEM
|
|
|
42
43
|
while (version < targetVersion) {
|
|
43
44
|
const step = MIGRATIONS.find((s) => s.from === version && s.to <= targetVersion);
|
|
44
45
|
if (!step) {
|
|
45
|
-
throw new UnsupportedSchemaError(version,
|
|
46
|
+
throw new UnsupportedSchemaError(version, migMsg('migrations.noPath', { from: version, to: targetVersion }));
|
|
46
47
|
}
|
|
47
48
|
if (step.to <= step.from) {
|
|
48
|
-
throw new Error(
|
|
49
|
+
throw new Error(migMsg('migrations.stepNotAdvanced', { from: step.from, to: step.to }));
|
|
49
50
|
}
|
|
50
51
|
current = step.migrate(current);
|
|
51
52
|
applied.push({ from: step.from, to: step.to });
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 迁移链领域消息目录(zh 源 / en 镜像)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不放在 src/core/messages-core.ts:架构边界(tests/architecture-boundaries.test.ts)
|
|
5
|
+
* 规定 src/migrations/** 只允许依赖 node 内置 / 自身 / ../schema / ../utils,
|
|
6
|
+
* **不得 import ../core** —— 迁移链必须保持对 core 零耦合(core 消费它,不能反过来)。
|
|
7
|
+
* 因此本目录自带一份零依赖的消息解析器:语言源由 core 侧接线(core → migrations 是允许方向,
|
|
8
|
+
* 见 src/core/messages-core.ts 的 setMigrationLanguageSource),未接线时 = zh
|
|
9
|
+
* (与迁移链改造前的行为逐字一致)。
|
|
10
|
+
*
|
|
11
|
+
* 纪律:
|
|
12
|
+
* - zh 值 = 改造前的中文字面量原样(逐字不变,既有测试断言依赖它);
|
|
13
|
+
* - en 值类型 Record<keyof typeof zh, string>(缺键/多键即编译错误),必须纯英文;
|
|
14
|
+
* - 动态明细(版本号、from/to)一律以 {param} 插值传入。
|
|
15
|
+
*/
|
|
16
|
+
export declare const zh: {
|
|
17
|
+
'migrations.stepConflict': string;
|
|
18
|
+
'migrations.schemaTooNew': string;
|
|
19
|
+
'migrations.schemaTooOld': string;
|
|
20
|
+
'migrations.noPath': string;
|
|
21
|
+
'migrations.stepNotAdvanced': string;
|
|
22
|
+
};
|
|
23
|
+
export declare const en: Record<keyof typeof zh, string>;
|
|
24
|
+
export type MigrationMessageKey = keyof typeof zh;
|
|
25
|
+
export type MigrationMessageParams = Readonly<Record<string, string | number>>;
|
|
26
|
+
/** core 侧接线:让迁移链文案跟随引擎语言(core → migrations 是允许方向)。 */
|
|
27
|
+
export declare function setMigrationLanguageSource(resolve: () => 'zh' | 'en'): void;
|
|
28
|
+
/**
|
|
29
|
+
* 迁移链的活翻译器:每次调用解析当前语言(默认 zh,绝不抛错),未知键回落键名。
|
|
30
|
+
* 与 src/core/engine-locale.ts 的 engineMsg 同口径,只是不依赖 core。
|
|
31
|
+
*/
|
|
32
|
+
export declare function migMsg(key: MigrationMessageKey, params?: MigrationMessageParams): string;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 迁移链领域消息目录(zh 源 / en 镜像)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么不放在 src/core/messages-core.ts:架构边界(tests/architecture-boundaries.test.ts)
|
|
5
|
+
* 规定 src/migrations/** 只允许依赖 node 内置 / 自身 / ../schema / ../utils,
|
|
6
|
+
* **不得 import ../core** —— 迁移链必须保持对 core 零耦合(core 消费它,不能反过来)。
|
|
7
|
+
* 因此本目录自带一份零依赖的消息解析器:语言源由 core 侧接线(core → migrations 是允许方向,
|
|
8
|
+
* 见 src/core/messages-core.ts 的 setMigrationLanguageSource),未接线时 = zh
|
|
9
|
+
* (与迁移链改造前的行为逐字一致)。
|
|
10
|
+
*
|
|
11
|
+
* 纪律:
|
|
12
|
+
* - zh 值 = 改造前的中文字面量原样(逐字不变,既有测试断言依赖它);
|
|
13
|
+
* - en 值类型 Record<keyof typeof zh, string>(缺键/多键即编译错误),必须纯英文;
|
|
14
|
+
* - 动态明细(版本号、from/to)一律以 {param} 插值传入。
|
|
15
|
+
*/
|
|
16
|
+
export const zh = {
|
|
17
|
+
'migrations.stepConflict': '迁移步骤冲突: {stepFrom}→{stepTo} 与 {dupFrom}→{dupTo} 重叠',
|
|
18
|
+
'migrations.schemaTooNew': '备份 schema v{from} 高于当前 v{target},需升级本插件',
|
|
19
|
+
'migrations.schemaTooOld': '备份 schema v{from} 低于最低支持 v{min}',
|
|
20
|
+
'migrations.noPath': '没有从 schema v{from} 到 v{to} 的迁移路径',
|
|
21
|
+
'migrations.stepNotAdvanced': '迁移步骤 {from}→{to} 未前进(注册表损坏)',
|
|
22
|
+
};
|
|
23
|
+
export const en = {
|
|
24
|
+
'migrations.stepConflict': 'Migration step conflict: {stepFrom}→{stepTo} overlaps {dupFrom}→{dupTo}',
|
|
25
|
+
'migrations.schemaTooNew': 'Backup schema v{from} is newer than the current v{target}; upgrade this plugin',
|
|
26
|
+
'migrations.schemaTooOld': 'Backup schema v{from} is older than the minimum supported v{min}',
|
|
27
|
+
'migrations.noPath': 'No migration path from schema v{from} to v{to}',
|
|
28
|
+
'migrations.stepNotAdvanced': 'Migration step {from}→{to} does not advance (corrupt registry)',
|
|
29
|
+
};
|
|
30
|
+
let languageSource = () => 'zh';
|
|
31
|
+
/** core 侧接线:让迁移链文案跟随引擎语言(core → migrations 是允许方向)。 */
|
|
32
|
+
export function setMigrationLanguageSource(resolve) {
|
|
33
|
+
languageSource = resolve;
|
|
34
|
+
}
|
|
35
|
+
/** 插值:{param} → 形参值;缺参原样保留(与 engine-locale.ts 的 interpolate 同口径)。 */
|
|
36
|
+
function interpolate(template, params) {
|
|
37
|
+
if (params === undefined)
|
|
38
|
+
return template;
|
|
39
|
+
return template.replace(/\{(\w+)\}/g, (match, key) => {
|
|
40
|
+
const value = params[key];
|
|
41
|
+
return value === undefined ? match : String(value);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 迁移链的活翻译器:每次调用解析当前语言(默认 zh,绝不抛错),未知键回落键名。
|
|
46
|
+
* 与 src/core/engine-locale.ts 的 engineMsg 同口径,只是不依赖 core。
|
|
47
|
+
*/
|
|
48
|
+
export function migMsg(key, params) {
|
|
49
|
+
let lang = 'zh';
|
|
50
|
+
try {
|
|
51
|
+
lang = languageSource() === 'en' ? 'en' : 'zh';
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
lang = 'zh';
|
|
55
|
+
}
|
|
56
|
+
return interpolate((lang === 'en' ? en : zh)[key], params);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=messages-migrations.js.map
|