dsh-config-manager 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -0
- package/README.zh-CN.md +218 -0
- package/cordis.patch.yml +45 -0
- package/dsh.bundle.patch +21 -0
- package/dsh.client +39 -0
- package/lib/adapters/agent-presets.d.ts +13 -0
- package/lib/adapters/agent-presets.js +14 -0
- package/lib/adapters/agent-presets.js.map +1 -0
- package/lib/adapters/credentials.d.ts +36 -0
- package/lib/adapters/credentials.js +121 -0
- package/lib/adapters/credentials.js.map +1 -0
- package/lib/adapters/file-collection.d.ts +14 -0
- package/lib/adapters/file-collection.js +102 -0
- package/lib/adapters/file-collection.js.map +1 -0
- package/lib/adapters/index.d.ts +38 -0
- package/lib/adapters/index.js +46 -0
- package/lib/adapters/index.js.map +1 -0
- package/lib/adapters/mcp.d.ts +27 -0
- package/lib/adapters/mcp.js +175 -0
- package/lib/adapters/mcp.js.map +1 -0
- package/lib/adapters/plugin-files.d.ts +15 -0
- package/lib/adapters/plugin-files.js +91 -0
- package/lib/adapters/plugin-files.js.map +1 -0
- package/lib/adapters/plugins.d.ts +13 -0
- package/lib/adapters/plugins.js +177 -0
- package/lib/adapters/plugins.js.map +1 -0
- package/lib/adapters/prompts.d.ts +29 -0
- package/lib/adapters/prompts.js +228 -0
- package/lib/adapters/prompts.js.map +1 -0
- package/lib/adapters/providers.d.ts +33 -0
- package/lib/adapters/providers.js +143 -0
- package/lib/adapters/providers.js.map +1 -0
- package/lib/adapters/sessions.d.ts +14 -0
- package/lib/adapters/sessions.js +15 -0
- package/lib/adapters/sessions.js.map +1 -0
- package/lib/adapters/settings.d.ts +23 -0
- package/lib/adapters/settings.js +142 -0
- package/lib/adapters/settings.js.map +1 -0
- package/lib/adapters/skills.d.ts +13 -0
- package/lib/adapters/skills.js +14 -0
- package/lib/adapters/skills.js.map +1 -0
- package/lib/adapters/test-helpers.d.ts +99 -0
- package/lib/adapters/test-helpers.js +192 -0
- package/lib/adapters/test-helpers.js.map +1 -0
- package/lib/adapters/ui.d.ts +26 -0
- package/lib/adapters/ui.js +88 -0
- package/lib/adapters/ui.js.map +1 -0
- package/lib/adapters/workspaces.d.ts +12 -0
- package/lib/adapters/workspaces.js +92 -0
- package/lib/adapters/workspaces.js.map +1 -0
- package/lib/client.d.ts +377 -0
- package/lib/client.js +2598 -0
- package/lib/client.js.map +1 -0
- package/lib/core/analyzer.d.ts +35 -0
- package/lib/core/analyzer.js +563 -0
- package/lib/core/analyzer.js.map +1 -0
- package/lib/core/backup.d.ts +29 -0
- package/lib/core/backup.js +184 -0
- package/lib/core/backup.js.map +1 -0
- package/lib/core/exporter.d.ts +37 -0
- package/lib/core/exporter.js +222 -0
- package/lib/core/exporter.js.map +1 -0
- package/lib/core/importer.d.ts +42 -0
- package/lib/core/importer.js +25 -0
- package/lib/core/importer.js.map +1 -0
- package/lib/core/index.d.ts +12 -0
- package/lib/core/index.js +11 -0
- package/lib/core/index.js.map +1 -0
- package/lib/core/rollback.d.ts +9 -0
- package/lib/core/rollback.js +136 -0
- package/lib/core/rollback.js.map +1 -0
- package/lib/core/types.d.ts +331 -0
- package/lib/core/types.js +18 -0
- package/lib/core/types.js.map +1 -0
- package/lib/core/validator.d.ts +18 -0
- package/lib/core/validator.js +77 -0
- package/lib/core/validator.js.map +1 -0
- package/lib/index.d.ts +51 -0
- package/lib/index.js +875 -0
- package/lib/index.js.map +1 -0
- package/lib/migrations/index.d.ts +29 -0
- package/lib/migrations/index.js +56 -0
- package/lib/migrations/index.js.map +1 -0
- package/lib/migrations/v1-to-v2.d.ts +9 -0
- package/lib/migrations/v1-to-v2.js +10 -0
- package/lib/migrations/v1-to-v2.js.map +1 -0
- package/lib/profiles/index.d.ts +4 -0
- package/lib/profiles/index.js +5 -0
- package/lib/profiles/index.js.map +1 -0
- package/lib/profiles/profile-manager.d.ts +102 -0
- package/lib/profiles/profile-manager.js +522 -0
- package/lib/profiles/profile-manager.js.map +1 -0
- package/lib/schema/config.d.ts +28 -0
- package/lib/schema/config.js +136 -0
- package/lib/schema/config.js.map +1 -0
- package/lib/schema/manifest.d.ts +28 -0
- package/lib/schema/manifest.js +109 -0
- package/lib/schema/manifest.js.map +1 -0
- package/lib/schema/types.d.ts +169 -0
- package/lib/schema/types.js +8 -0
- package/lib/schema/types.js.map +1 -0
- package/lib/schema/versions.d.ts +27 -0
- package/lib/schema/versions.js +50 -0
- package/lib/schema/versions.js.map +1 -0
- package/lib/security/encryption.d.ts +41 -0
- package/lib/security/encryption.js +152 -0
- package/lib/security/encryption.js.map +1 -0
- package/lib/security/index.d.ts +16 -0
- package/lib/security/index.js +17 -0
- package/lib/security/index.js.map +1 -0
- package/lib/security/integrity.d.ts +30 -0
- package/lib/security/integrity.js +78 -0
- package/lib/security/integrity.js.map +1 -0
- package/lib/security/redaction.d.ts +26 -0
- package/lib/security/redaction.js +95 -0
- package/lib/security/redaction.js.map +1 -0
- package/lib/security/secret-scanner.d.ts +48 -0
- package/lib/security/secret-scanner.js +253 -0
- package/lib/security/secret-scanner.js.map +1 -0
- package/lib/security/zip-security.d.ts +27 -0
- package/lib/security/zip-security.js +154 -0
- package/lib/security/zip-security.js.map +1 -0
- package/lib/ui/conflict-view.d.ts +28 -0
- package/lib/ui/conflict-view.js +49 -0
- package/lib/ui/conflict-view.js.map +1 -0
- package/lib/ui/errors.d.ts +17 -0
- package/lib/ui/errors.js +76 -0
- package/lib/ui/errors.js.map +1 -0
- package/lib/ui/export-flow.d.ts +62 -0
- package/lib/ui/export-flow.js +79 -0
- package/lib/ui/export-flow.js.map +1 -0
- package/lib/ui/import-wizard.d.ts +67 -0
- package/lib/ui/import-wizard.js +169 -0
- package/lib/ui/import-wizard.js.map +1 -0
- package/lib/ui/path-mapping.d.ts +38 -0
- package/lib/ui/path-mapping.js +75 -0
- package/lib/ui/path-mapping.js.map +1 -0
- package/lib/ui/progress.d.ts +27 -0
- package/lib/ui/progress.js +76 -0
- package/lib/ui/progress.js.map +1 -0
- package/lib/ui/report.d.ts +21 -0
- package/lib/ui/report.js +146 -0
- package/lib/ui/report.js.map +1 -0
- package/lib/ui/test-helpers.d.ts +62 -0
- package/lib/ui/test-helpers.js +147 -0
- package/lib/ui/test-helpers.js.map +1 -0
- package/lib/ui/types.d.ts +129 -0
- package/lib/ui/types.js +21 -0
- package/lib/ui/types.js.map +1 -0
- package/lib/utils/hashing.d.ts +17 -0
- package/lib/utils/hashing.js +41 -0
- package/lib/utils/hashing.js.map +1 -0
- package/lib/utils/json.d.ts +24 -0
- package/lib/utils/json.js +76 -0
- package/lib/utils/json.js.map +1 -0
- package/lib/utils/logger.d.ts +39 -0
- package/lib/utils/logger.js +82 -0
- package/lib/utils/logger.js.map +1 -0
- package/lib/utils/paths.d.ts +31 -0
- package/lib/utils/paths.js +132 -0
- package/lib/utils/paths.js.map +1 -0
- package/lib/utils/zip.d.ts +54 -0
- package/lib/utils/zip.js +285 -0
- package/lib/utils/zip.js.map +1 -0
- package/package.json +109 -0
- package/src/adapters/agent-presets.ts +14 -0
- package/src/adapters/credentials.test.ts +90 -0
- package/src/adapters/credentials.ts +141 -0
- package/src/adapters/file-collection.ts +110 -0
- package/src/adapters/files.test.ts +113 -0
- package/src/adapters/index.ts +69 -0
- package/src/adapters/mcp.test.ts +96 -0
- package/src/adapters/mcp.ts +181 -0
- package/src/adapters/plugin-files.ts +96 -0
- package/src/adapters/plugins.test.ts +72 -0
- package/src/adapters/plugins.ts +176 -0
- package/src/adapters/prompts.test.ts +110 -0
- package/src/adapters/prompts.ts +226 -0
- package/src/adapters/providers.test.ts +125 -0
- package/src/adapters/providers.ts +161 -0
- package/src/adapters/roundtrip.test.ts +188 -0
- package/src/adapters/sessions.ts +15 -0
- package/src/adapters/settings.test.ts +93 -0
- package/src/adapters/settings.ts +165 -0
- package/src/adapters/skills.ts +14 -0
- package/src/adapters/test-helpers.ts +207 -0
- package/src/adapters/ui.test.ts +56 -0
- package/src/adapters/ui.ts +106 -0
- package/src/adapters/workspaces.test.ts +79 -0
- package/src/adapters/workspaces.ts +95 -0
- package/src/client/CLIENT_DEPENDENCIES.md +101 -0
- package/src/client/ConfigManagerSection.tsx +60 -0
- package/src/client/api.ts +270 -0
- package/src/client/client-types.ts +27 -0
- package/src/client/common/ErrorBanner.tsx +68 -0
- package/src/client/common/ProgressBar.tsx +49 -0
- package/src/client/common/ReportView.tsx +117 -0
- package/src/client/common/ui.tsx +165 -0
- package/src/client/config-manager.module.css +759 -0
- package/src/client/css-modules.d.ts +8 -0
- package/src/client/export/ExportView.tsx +222 -0
- package/src/client/import/ConflictList.tsx +72 -0
- package/src/client/import/ImportWizardView.tsx +406 -0
- package/src/client/import/PathMappingForm.tsx +77 -0
- package/src/client/index.ts +65 -0
- package/src/client/locales.ts +182 -0
- package/src/core/analyzer.ts +631 -0
- package/src/core/backup.ts +212 -0
- package/src/core/exporter.ts +246 -0
- package/src/core/importer.ts +66 -0
- package/src/core/index.ts +39 -0
- package/src/core/rollback.ts +140 -0
- package/src/core/smoke.test.ts +717 -0
- package/src/core/types.ts +343 -0
- package/src/core/validator.ts +84 -0
- package/src/index.ts +942 -0
- package/src/migrations/index.ts +71 -0
- package/src/migrations/v1-to-v2.ts +17 -0
- package/src/profiles/index.ts +8 -0
- package/src/profiles/profile-manager.test.ts +280 -0
- package/src/profiles/profile-manager.ts +606 -0
- package/src/schema/config.ts +137 -0
- package/src/schema/manifest.ts +124 -0
- package/src/schema/types.ts +179 -0
- package/src/schema/versions.ts +55 -0
- package/src/security/encryption.ts +180 -0
- package/src/security/index.ts +16 -0
- package/src/security/integrity.ts +102 -0
- package/src/security/redaction.ts +101 -0
- package/src/security/secret-scanner.ts +275 -0
- package/src/security/security.test.ts +796 -0
- package/src/security/zip-security.ts +180 -0
- package/src/ui/conflict-view.test.ts +62 -0
- package/src/ui/conflict-view.ts +65 -0
- package/src/ui/errors.test.ts +44 -0
- package/src/ui/errors.ts +95 -0
- package/src/ui/export-flow.test.ts +75 -0
- package/src/ui/export-flow.ts +128 -0
- package/src/ui/import-wizard.test.ts +128 -0
- package/src/ui/import-wizard.ts +208 -0
- package/src/ui/path-mapping.test.ts +67 -0
- package/src/ui/path-mapping.ts +103 -0
- package/src/ui/progress.test.ts +42 -0
- package/src/ui/progress.ts +90 -0
- package/src/ui/report.test.ts +93 -0
- package/src/ui/report.ts +140 -0
- package/src/ui/test-helpers.ts +177 -0
- package/src/ui/types.ts +193 -0
- package/src/utils/hashing.ts +53 -0
- package/src/utils/json.ts +77 -0
- package/src/utils/logger.ts +114 -0
- package/src/utils/paths.ts +122 -0
- package/src/utils/zip.ts +332 -0
package/README.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# DSH Config Manager
|
|
2
|
+
|
|
3
|
+
**Backup · Export · Import · Migrate · Restore** — A configuration backup / export / import / migration manager for DSH.
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
One-click export of your main DSH configuration to a ZIP file, import it on another DSH, and restore your working environment as completely as possible.
|
|
8
|
+
|
|
9
|
+
> ⚠️ **Security first: no Secret (API Key / Token / Password) is exported by default.** See [Security](#security).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
DSH configuration is a hybrid model — one central `settings.yaml` plus multiple standalone files plus plugin-owned files (see `Docs/research/dsh-architecture.md`).
|
|
16
|
+
This plugin does **not** copy `~/.dsh` wholesale. Instead it collects configuration by real config categories, packages them into a ZIP backup with a manifest and checksums, and runs a safe import flow on the target side:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Analyze → Preview → Snapshot → Apply → Validate → Rollback(if needed)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **Export**: Quick Export (one-click recommended config) and per-section custom export.
|
|
25
|
+
- **Import**: ZIP validation → manifest read → integrity check → schema check → compatibility check → content scan → import-plan preview → user confirmation → automatic snapshot → apply → validate → result (automatic rollback on failure).
|
|
26
|
+
- **Dry Run / Preview**: `analyzeImport()` + `createImportPlan()` are pure computation with zero writes — full preview before importing.
|
|
27
|
+
- **Conflict handling**: global strategies `merge` (default) / `replace` / `skipExisting` plus per-item `Keep Current / Use Imported / Review`.
|
|
28
|
+
- **Path mapping**: cross-device absolute-path detection and batch prefix mapping (workspace paths / MCP cwd / plugin config paths).
|
|
29
|
+
- **Secret safety**: all sensitive fields are stripped by default; encrypted full backup (scrypt + AES-256-GCM) is an opt-in advanced feature.
|
|
30
|
+
- **Automatic snapshot & rollback**: backs up the target state before import and restores it in reverse order on failure.
|
|
31
|
+
- **Schema versioning & migration**: independent `schemaVersion`, migration logic centralized in `src/migrations/`.
|
|
32
|
+
- **Idempotency**: re-importing the same ZIP creates no duplicates (keyed by Plugin ID / MCP serverName / Prompt name / Workspace id / Credential ref).
|
|
33
|
+
- **Compatibility score**: Excellent / Good / Partial / Unsupported (rule-driven).
|
|
34
|
+
- **Profiles**: save current config as a Profile / switch / duplicate / rename / export / import / delete; switching includes Preview + snapshot + rollback (`src/profiles/`).
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
This plugin is a standard **DSH bundle plugin** (mirrors the dsh-ssh engineering pattern, research report §5.1): `package.json` declares
|
|
39
|
+
`dsh.bundle.patch` (pointing to `cordis.patch.yml`, the CLI bundle hard criterion) and `dsh.client` (browser-half declaration);
|
|
40
|
+
`npm run build` produces both halves (`lib/index.js` host half + `lib/client.js` browser half, the latter loaded into the Web GUI via
|
|
41
|
+
`window.__ModuleLoader__.load(...)`).
|
|
42
|
+
|
|
43
|
+
Two install options (choose one):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# ① Install from a local tgz / directory (recommended, after build)
|
|
47
|
+
npm run build
|
|
48
|
+
npm pack # produces dsh-config-manager-0.1.0.tgz
|
|
49
|
+
dsh plugin --profile web add file:/absolute/path/to/dsh-config-manager-0.1.0.tgz
|
|
50
|
+
|
|
51
|
+
# ② Install from a registry after publishing
|
|
52
|
+
dsh plugin --profile web add dsh-config-manager
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
> **`--legacy-peer-deps` note**: some DSH core packages in peerDependencies (e.g.
|
|
56
|
+
> `@deepseek-ai/dsh-plugin-marketplace`, `dsh-host-plugin-inventory`) are not published to the public npm registry
|
|
57
|
+
> and only exist in a local DSH profile. If npm/pnpm fails while resolving peer dependencies, skip automatic peer installation:
|
|
58
|
+
> - Direct npm install: `npm install --legacy-peer-deps`
|
|
59
|
+
> - `dsh plugin add` (forwards to pnpm internally): `dsh plugin --profile web add <spec> --config.auto-install-peers=false`
|
|
60
|
+
>
|
|
61
|
+
> At runtime these packages are provided by the DSH profile itself (peerDependencies semantics); the plugin never reinstalls them.
|
|
62
|
+
|
|
63
|
+
> **Local verification tip**: isolate testing with the `$DSH_HOME` environment variable — never touches `~/.dsh`:
|
|
64
|
+
> ```bash
|
|
65
|
+
> $env:DSH_HOME = "D:\tmp\dsh-home" # Windows PowerShell
|
|
66
|
+
> dsh plugin --profile test add file:<tgz> --config.auto-install-peers=false
|
|
67
|
+
> dsh --profile test --dump-config | Select-String config-manager # should show the mount line
|
|
68
|
+
> ```
|
|
69
|
+
|
|
70
|
+
## Export
|
|
71
|
+
|
|
72
|
+
Two modes:
|
|
73
|
+
|
|
74
|
+
- **Quick Export**: one-click export of the recommended sections (settings / ui / providers / plugins / mcp / prompts / skills / agentPresets / workspaces / credentialsStatus).
|
|
75
|
+
- **Custom Export**: choose sections individually (`pluginFiles` and `sessions` are opt-in; `sessions` is off by default).
|
|
76
|
+
|
|
77
|
+
Output: `dsh-config-<yyyy-MM-dd>.zip` containing `manifest.json` + per-section data + `integrity/checksums.json` (SHA-256).
|
|
78
|
+
|
|
79
|
+
## Import
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Select ZIP → Validate ZIP → Read Manifest → Check Integrity → Check Schema
|
|
83
|
+
→ Check Compatibility → Scan Contents → Generate Import Plan → Show Preview
|
|
84
|
+
→ User Confirms → Create Backup (Snapshot) → Import → Validate → Show Result
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Import flow is enforced: **no writes happen before confirmation**; **a snapshot is always created before importing**; on failure `rollbackOnError` decides between full rollback or per-item honest reporting.
|
|
88
|
+
|
|
89
|
+
## Security
|
|
90
|
+
|
|
91
|
+
> **The default backup contains no Secret values.** This is a hard security invariant, enforced by the `Exporter`:
|
|
92
|
+
|
|
93
|
+
- All structured section data passes a sensitive-field scan before being written to the ZIP (field-name blacklist: password / token / apiKey / secret / credential / authorization / cookie / privateKey / clientSecret etc., case-insensitive); matches are stripped.
|
|
94
|
+
- `ctx.settings.describe({ redactSecrets: true })` is the first line of defense for DSH-known secrets; the sensitive-field scanner is the second line of defense for plugin-defined fields.
|
|
95
|
+
- Credentials (`.credentials.yaml`) **never export values**, only state (`{ref, required, configured, hasValue:false}`); after import a "N credentials need attention" list is generated.
|
|
96
|
+
- **Encrypted full backup (optional)**: when "Include secrets" is explicitly checked, a backup password is required; `node:crypto` (scrypt KDF + AES-256-GCM) is used, **the password is never written to the manifest**; `secrets.enc` is only written back via `ctx.credentials.set()` after decryption.
|
|
97
|
+
- Without an encryption provider, `includeSecrets: true` is rejected (secrets are never leaked in plaintext).
|
|
98
|
+
- Logging is fully redacted — Secret values never reach logs.
|
|
99
|
+
- A ZIP is untrusted input: defends against Zip Slip / absolute paths / symlinks / zip bombs (entry count / compressed size / uncompressed size / compression-ratio limits) / malformed ZIPs / checksum mismatch — any trigger rejects the whole archive.
|
|
100
|
+
|
|
101
|
+
## What is NOT exported
|
|
102
|
+
|
|
103
|
+
By default **not** exported (spec §34.19/20):
|
|
104
|
+
|
|
105
|
+
- API Key / Password / Token / Cookie / Session / auth credentials (values)
|
|
106
|
+
- `~/.dsh/.anonymous-user-id` (device unique ID)
|
|
107
|
+
- Conversation history (`sessions/`, off by default; v1 supports file-level copy only)
|
|
108
|
+
- Logs / Cache / temp files
|
|
109
|
+
- Browser localStorage UI state (no host-side channel; only `uiMigrationNotes` is exported)
|
|
110
|
+
- Plugin binaries (never packaged — only the manifest is migrated, install goes through the official mechanism)
|
|
111
|
+
|
|
112
|
+
## Secrets
|
|
113
|
+
|
|
114
|
+
| Backup type | Import behavior |
|
|
115
|
+
|---|---|
|
|
116
|
+
| Normal backup (no secrets.enc) | All credentials → `MissingSecret`, filled in by the user after import |
|
|
117
|
+
| Encrypted backup + correct password | Auto-decrypted and restored via `credentials.set()` (per-item confirmation in preview) |
|
|
118
|
+
| Encrypted backup + no password | Same as normal backup: state-only, user fills in |
|
|
119
|
+
|
|
120
|
+
## Compatibility
|
|
121
|
+
|
|
122
|
+
| Status | Rule |
|
|
123
|
+
|---|---|
|
|
124
|
+
| Excellent | Same platform, no missing sections, supported schema |
|
|
125
|
+
| Good | Backup from an older DSH (target is backward compatible) |
|
|
126
|
+
| Partial | Cross-platform / missing sections / backup newer than target |
|
|
127
|
+
| Unsupported | Schema beyond the supported range |
|
|
128
|
+
|
|
129
|
+
## Backup format
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
dsh-config-2026-08-14.zip
|
|
133
|
+
├── manifest.json # schemaVersion / exporter / source / sections / security
|
|
134
|
+
├── config/settings.json # non-UI settings namespaces (redacted + revision)
|
|
135
|
+
├── config/ui.json # UI namespaces + uiMigrationNotes
|
|
136
|
+
├── ai/providers.json # llm-* providers/models (same section, not split)
|
|
137
|
+
├── plugins/plugins.json + patch.json
|
|
138
|
+
├── mcp/servers.json # dsh-mcp-client entries extracted from the composed patch
|
|
139
|
+
├── custom/prompts.json + skills/
|
|
140
|
+
├── agents/presets/
|
|
141
|
+
├── workspaces/workspaces.json
|
|
142
|
+
├── plugin-files/ # optional
|
|
143
|
+
├── security/credentials.json # credential state (never contains values)
|
|
144
|
+
├── security/secrets.enc # encrypted backups only
|
|
145
|
+
└── integrity/checksums.json # SHA-256
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Development
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install --legacy-peer-deps # peers include DSH core packages not on the public registry, see Installation
|
|
152
|
+
npm run typecheck # tsc --noEmit
|
|
153
|
+
npm run build # tsc -p tsconfig.build.json (host half lib/) + tsdown (client bundle lib/client.js)
|
|
154
|
+
npm run bundle # rebuild the client bundle only (tsdown)
|
|
155
|
+
npm test # node --test "src/**/*.test.ts" "tests/**/*.test.ts"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Architecture: the core engine (`src/core`) depends only on the `ConfigAdapter` / `HostContext` interfaces (decoupled from the DSH runtime, testable with in-memory mocks); `src/adapters` implements each config category; `src/security` provides secret scanning / encryption / integrity / ZIP safety / redaction; `src/migrations` centralizes schema migration.
|
|
159
|
+
|
|
160
|
+
## Testing
|
|
161
|
+
|
|
162
|
+
Test framework: **node:test (Node built-in, zero dependency)**, following the choice made in the core module (no vitest). Tests live in `src/**/*.test.ts` and `tests/**/*.test.ts`.
|
|
163
|
+
|
|
164
|
+
Coverage matrix (spec §33 + acceptance scenarios A–G):
|
|
165
|
+
|
|
166
|
+
| Group | Coverage |
|
|
167
|
+
|---|---|
|
|
168
|
+
| Export | normal / empty / large (1MB+) / Unicode / special characters / secret filtering |
|
|
169
|
+
| Import | normal / Merge / Replace / Skip (never deletes target-only items, §32) / Conflict / Missing plugin / Missing dependency / Missing secret / unconfirmed rejection |
|
|
170
|
+
| Rollback (scenario E) | multi-adapter mid-flight failure → full restore (settings / file blobs / workspace / patch lines); `rollbackOnError=false` comparison; honest partial-rollback report |
|
|
171
|
+
| Migration (scenario G) | `migrateToCurrent` mechanism-level: same version / too new / below minimum / no path / registry overlap / chained progression (**honest note: v1 is current, no real v2 exists for end-to-end verification**) |
|
|
172
|
+
| Security (scenario F) | malformed ZIP / oversized entry count / checksum mismatch & missing / Zip Slip / absolute paths |
|
|
173
|
+
| Cross-platform (scenario B) | win32→darwin / darwin→win32 / linux→win32 batch prefix mapping |
|
|
174
|
+
| Redaction | log messages / meta / full pipeline never leaks secret values |
|
|
175
|
+
| Schema | manifest structure validation / version predicate functions |
|
|
176
|
+
|
|
177
|
+
Current test results: **186 tests, all passing** (`npm test`); `npm run typecheck` and `npm run build` both pass.
|
|
178
|
+
|
|
179
|
+
## Known limitations
|
|
180
|
+
|
|
181
|
+
1. **Workspace: create/rename title only**: DSH's workspace service has no "overwrite whole" write channel — import can create workspaces and update titles; paths and session lists are maintained by DSH itself from the real directory, cross-device paths are adapted via path mapping.
|
|
182
|
+
2. **Some DSH core packages are not on the public npm registry** (e.g. `@deepseek-ai/dsh-plugin-marketplace`, `dsh-host-plugin-inventory`): features depending on their APIs only work in a local profile; installing this plugin requires skipping automatic peer installation (see the `--legacy-peer-deps` note in [Installation](#installation)).
|
|
183
|
+
3. **No MCP management API** (research report §4.3): MCP is imported as composed patch lines and takes effect after restarting DSH; no add/remove/update API.
|
|
184
|
+
4. **Plugin installation requires a restart**: `pluginMarketplace.installPlugin` only returns `needsRestart`; restarting depends on DSH Desktop.
|
|
185
|
+
5. **Browser localStorage UI state is not migrated** (task board data, panel widths, etc.): no host channel.
|
|
186
|
+
6. **keybindings / workflow configs / commands / rules files**: DSH currently has no such concepts; no sections are implemented (nothing invented).
|
|
187
|
+
7. **Credential values cannot be rolled back**: DSH never reads credential values back; credentials overwritten during import can only be marked `manualHint` for manual re-entry on rollback.
|
|
188
|
+
8. **Newly created items cannot be rollback-deleted**: DSH settings have no delete semantics; namespaces newly created by import can only be handled manually on rollback (honestly reported as partial).
|
|
189
|
+
9. **Schema migration**: v1→v2 is a placeholder (current `CURRENT_SCHEMA_VERSION=1`); the mechanism is ready but no real v2 exists to verify.
|
|
190
|
+
10. **History/session migration**: off by default; v1 supports file-level copy only.
|
|
191
|
+
11. **Encrypted backups**: depend on a strong user-set password; a lost password makes `secrets.enc` undecryptable (by design).
|
|
192
|
+
|
|
193
|
+
## Manual Test (shortest manual flow)
|
|
194
|
+
|
|
195
|
+
> Prerequisites: two DSH instances (or two config directories on one machine); this plugin built and installed per [Installation](#installation).
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
DSH A
|
|
199
|
+
→ open Config Manager → Export Configuration
|
|
200
|
+
→ choose Quick Export → export dsh-config-<date>.zip (confirm in the report that Secrets are all excluded)
|
|
201
|
+
→ copy the ZIP to DSH B
|
|
202
|
+
|
|
203
|
+
DSH B
|
|
204
|
+
→ open Config Manager → Import Configuration
|
|
205
|
+
→ select the ZIP → wait for Analyzing... → review the Import Preview (sections/plugins/path mapping/credential re-entry list)
|
|
206
|
+
→ if there are path issues → choose mapping directories (batch prefix mapping)
|
|
207
|
+
→ resolve conflicts (Keep Current / Use Imported / Review)
|
|
208
|
+
→ confirm Import → watch progress → review the result report
|
|
209
|
+
→ fill in missing credentials (N credentials need attention)
|
|
210
|
+
→ Verify: settings / plugins / MCP / Prompts / Skills / Workspaces are restored;
|
|
211
|
+
if the import failed midway → confirm it rolled back automatically and the original config still works
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
**Product principles**: better to migrate one config less than to break a user's existing config. Every Import follows `Analyze → Preview → Backup → Modify → Validate → Rollback`; every Secret follows `never export by default / never log / never expose / never silently transfer`.
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# DSH Config Manager
|
|
2
|
+
|
|
3
|
+
**Backup · Export · Import · Migrate · Restore** —— DSH 配置备份 / 导出 / 导入 / 迁移管理器。
|
|
4
|
+
|
|
5
|
+
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
6
|
+
|
|
7
|
+
在一台 DSH 中一键导出主要配置为 ZIP,在另一台 DSH 中导入并尽可能恢复原来的使用环境。
|
|
8
|
+
|
|
9
|
+
> ⚠️ **安全第一:默认不导出任何 Secret(API Key / Token / 密码)。** 详见 [Security](#security)。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
DSH 的配置是「一个集中 `settings.yaml` + 多个独立文件 + 插件自有文件」的混合模型(见 `Docs/research/dsh-architecture.md`)。
|
|
16
|
+
本插件**不整包复制 `~/.dsh`**,而是按真实配置类别分区收集、打包为带清单(manifest)与校验和(checksums)的 ZIP 备份,并在导入端执行
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Analyze → Preview → Snapshot → Apply → Validate → Rollback(if needed)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
的安全导入流程。
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **Export**:Quick Export(推荐配置一键导出)与按分区选择导出。
|
|
27
|
+
- **Import**:ZIP 校验 → manifest 读取 → 完整性校验 → schema 检查 → 兼容性检查 → 内容扫描 → 导入计划预览 → 用户确认 → 自动快照 → 执行 → 校验 → 结果(失败自动回滚)。
|
|
28
|
+
- **Dry Run / Preview**:`analyzeImport()` + `createImportPlan()` 纯计算零写入,导入前完整预览。
|
|
29
|
+
- **冲突处理**:`merge`(默认)/ `replace` / `skipExisting` 全局策略 + 逐项 `Keep Current / Use Imported / Review`。
|
|
30
|
+
- **路径映射**:跨设备绝对路径检测与批量前缀映射(workspace 路径 / MCP cwd / 插件配置路径)。
|
|
31
|
+
- **Secret 安全**:默认剥离所有敏感字段;加密完整备份(scrypt + AES-256-GCM)为可选高级功能。
|
|
32
|
+
- **自动快照与回滚**:导入前备份将被修改的目标,失败时逆序补偿恢复。
|
|
33
|
+
- **Schema 版本与迁移**:`schemaVersion` 独立演进,迁移逻辑集中在 `src/migrations/`。
|
|
34
|
+
- **幂等**:同一 ZIP 重复导入不产生重复数据(按 Plugin ID / MCP serverName / Prompt name / Workspace id / Credential ref 识别)。
|
|
35
|
+
- **兼容性评分**:Excellent / Good / Partial / Unsupported(规则驱动)。
|
|
36
|
+
- **Profiles(配置 Profile)**:保存当前配置为 Profile / 切换 / 复制 / 重命名 / 导出 / 导入 / 删除;切换带 Preview + 快照 + 回滚(`src/profiles/`)。
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
本插件是标准的 **DSH bundle 插件**(仿 dsh-ssh 工程范式,见研究报告 §5.1):`package.json` 声明
|
|
41
|
+
`dsh.bundle.patch`(指向 `cordis.patch.yml`,CLI 的 bundle 硬判据)与 `dsh.client`(浏览器半声明),
|
|
42
|
+
`npm run build` 产出双半产物(`lib/index.js` Host 半 + `lib/client.js` 浏览器半,后者以
|
|
43
|
+
`window.__ModuleLoader__.load(...)` 装载进 Web GUI)。
|
|
44
|
+
|
|
45
|
+
两种安装方式(二选一):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# ① 从本地 tgz / 目录安装(推荐,构建后)
|
|
49
|
+
npm run build
|
|
50
|
+
npm pack # 产出 dsh-config-manager-0.1.0.tgz
|
|
51
|
+
dsh plugin --profile web add file:/absolute/path/to/dsh-config-manager-0.1.0.tgz
|
|
52
|
+
|
|
53
|
+
# ② 发布后从 registry 安装
|
|
54
|
+
dsh plugin --profile web add dsh-config-manager
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> **`--legacy-peer-deps` 说明**:peerDependencies 中的部分 DSH 核心包(如
|
|
58
|
+
> `@deepseek-ai/dsh-plugin-marketplace`、`dsh-host-plugin-inventory`)尚未发布到公共 npm registry,
|
|
59
|
+
> 只在本地 DSH profile 环境存在。安装时若 npm/pnpm 尝试解析 peer 依赖并失败,请跳过 peer 自动安装:
|
|
60
|
+
> - npm 直接安装:`npm install --legacy-peer-deps`
|
|
61
|
+
> - `dsh plugin add`(内部转发 pnpm):`dsh plugin --profile web add <spec> --config.auto-install-peers=false`
|
|
62
|
+
>
|
|
63
|
+
> 运行时这些包由 DSH profile 自身提供(peerDependencies 语义),插件不重复安装。
|
|
64
|
+
|
|
65
|
+
> **本地验证提示**:可用 `$DSH_HOME=<临时目录>` 环境变量隔离测试,完全不触碰 `~/.dsh`:
|
|
66
|
+
> ```bash
|
|
67
|
+
> $env:DSH_HOME = "D:\tmp\dsh-home" # Windows PowerShell
|
|
68
|
+
> dsh plugin --profile test add file:<tgz> --config.auto-install-peers=false
|
|
69
|
+
> dsh --profile test --dump-config | Select-String config-manager # 应看到挂载行
|
|
70
|
+
> ```
|
|
71
|
+
|
|
72
|
+
## Export
|
|
73
|
+
|
|
74
|
+
两种方式:
|
|
75
|
+
|
|
76
|
+
- **Quick Export**:一键导出推荐分区(settings / ui / providers / plugins / mcp / prompts / skills / agentPresets / workspaces / credentialsStatus)。
|
|
77
|
+
- **Custom Export**:按分区逐项选择(可选 `pluginFiles`、`sessions`;`sessions` 默认关闭)。
|
|
78
|
+
|
|
79
|
+
导出产物:`dsh-config-<yyyy-MM-dd>.zip`,含 `manifest.json` + 各分区数据 + `integrity/checksums.json`(SHA-256)。
|
|
80
|
+
|
|
81
|
+
## Import
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
Select ZIP → Validate ZIP → Read Manifest → Check Integrity → Check Schema
|
|
85
|
+
→ Check Compatibility → Scan Contents → Generate Import Plan → Show Preview
|
|
86
|
+
→ User Confirms → Create Backup (Snapshot) → Import → Validate → Show Result
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
导入流程强制:**未确认不执行任何写入**;**导入前必须生成快照**;失败按 `rollbackOnError` 决定整体回滚或单项如实记录。
|
|
90
|
+
|
|
91
|
+
## Security
|
|
92
|
+
|
|
93
|
+
> **默认备份不包含任何 Secret 值。** 这是硬性安全不变量,由 `Exporter` 强制:
|
|
94
|
+
|
|
95
|
+
- 所有结构化分区数据在写入 ZIP 前经过敏感字段扫描(字段名黑名单:password / token / apiKey / secret / credential / authorization / cookie / privateKey / clientSecret 等,大小写不敏感),命中即剥离。
|
|
96
|
+
- `ctx.settings.describe({ redactSecrets: true })` 作为第一道防线剥离 DSH 已知秘密;敏感字段扫描器作为第二道防线兜底插件自定义字段。
|
|
97
|
+
- 凭据(`.credentials.yaml`)**永不导出值**,只导出状态(`{ref, required, configured, hasValue:false}`),导入后生成「N credentials need attention」补录清单。
|
|
98
|
+
- **加密完整备份(可选)**:显式勾选「Include secrets」时,要求设置备份密码,使用 `node:crypto`(scrypt 派生 + AES-256-GCM 加密),**密码绝不写入 manifest**;`secrets.enc` 只有解密后经 `ctx.credentials.set()` 写回。
|
|
99
|
+
- 无加密提供者时 `includeSecrets: true` 会被拒绝(绝不明文泄密)。
|
|
100
|
+
- 日志系统全部经过 redaction,Secret 值永不进入日志。
|
|
101
|
+
- ZIP 属于不可信输入:防御 Zip Slip / 绝对路径 / 符号链接 / zip bomb(条目数 / 压缩体积 / 解压体积 / 压缩比上限)/ 畸形 ZIP / checksum 不匹配,任何一条触发即整体拒绝。
|
|
102
|
+
|
|
103
|
+
## What is NOT exported
|
|
104
|
+
|
|
105
|
+
默认**不**导出(规范 §34.19/20):
|
|
106
|
+
|
|
107
|
+
- API Key / Password / Token / Cookie / Session / 认证凭据(值)
|
|
108
|
+
- `~/.dsh/.anonymous-user-id`(设备唯一 ID)
|
|
109
|
+
- 会话历史(`sessions/`,默认关闭;v1 仅支持文件级复制)
|
|
110
|
+
- Logs / Cache / 临时文件
|
|
111
|
+
- 浏览器 localStorage 中的 UI 状态(Host 侧无通道,仅导出 `uiMigrationNotes` 说明)
|
|
112
|
+
- 插件二进制(绝不打包,只迁移清单并走官方安装机制)
|
|
113
|
+
|
|
114
|
+
## Secrets
|
|
115
|
+
|
|
116
|
+
| 备份类型 | 导入行为 |
|
|
117
|
+
|---|---|
|
|
118
|
+
| 普通备份(无 secrets.enc) | 全部凭据 → `MissingSecret`,导入后由用户补录 |
|
|
119
|
+
| 加密备份 + 正确密码 | 自动解密并经 `credentials.set()` 恢复(预览时可逐条确认) |
|
|
120
|
+
| 加密备份 + 不输密码 | 同普通备份:状态补录 |
|
|
121
|
+
|
|
122
|
+
## Compatibility
|
|
123
|
+
|
|
124
|
+
| 状态 | 规则 |
|
|
125
|
+
|---|---|
|
|
126
|
+
| Excellent | 同平台、无分区缺失、schema 受支持 |
|
|
127
|
+
| Good | 备份来自更旧 DSH(目标向后兼容) |
|
|
128
|
+
| Partial | 跨平台 / 分区缺失 / 备份比目标新 |
|
|
129
|
+
| Unsupported | schema 超出支持范围 |
|
|
130
|
+
|
|
131
|
+
## Backup format
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
dsh-config-2026-08-14.zip
|
|
135
|
+
├── manifest.json # schemaVersion / exporter / source / sections / security
|
|
136
|
+
├── config/settings.json # 非 UI settings namespace(redacted + revision)
|
|
137
|
+
├── config/ui.json # UI namespace + uiMigrationNotes
|
|
138
|
+
├── ai/providers.json # llm-* providers/models(同 section 不拆分)
|
|
139
|
+
├── plugins/plugins.json + patch.json
|
|
140
|
+
├── mcp/servers.json # 组合 patch 提取的 dsh-mcp-client 条目
|
|
141
|
+
├── custom/prompts.json + skills/
|
|
142
|
+
├── agents/presets/
|
|
143
|
+
├── workspaces/workspaces.json
|
|
144
|
+
├── plugin-files/ # 可选
|
|
145
|
+
├── security/credentials.json # 凭据状态(永不含值)
|
|
146
|
+
├── security/secrets.enc # 仅加密备份
|
|
147
|
+
└── integrity/checksums.json # SHA-256
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Development
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
npm install --legacy-peer-deps # peer 含未发布公共 registry 的 DSH 核心包,见 Installation
|
|
154
|
+
npm run typecheck # tsc --noEmit
|
|
155
|
+
npm run build # tsc -p tsconfig.build.json(Host 半 lib/)+ tsdown(client bundle lib/client.js)
|
|
156
|
+
npm run bundle # 仅重新构建 client bundle(tsdown)
|
|
157
|
+
npm test # node --test "src/**/*.test.ts" "tests/**/*.test.ts"
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
架构:核心引擎(`src/core`)只依赖 `ConfigAdapter` / `HostContext` 接口(与 DSH 运行时解耦,可用内存 mock 测试);`src/adapters` 实现各配置类别;`src/security` 提供秘密扫描 / 加密 / 完整性 / ZIP 安全 / redaction;`src/migrations` 集中 schema 迁移。
|
|
161
|
+
|
|
162
|
+
## Testing
|
|
163
|
+
|
|
164
|
+
测试框架为 **node:test(Node 内置,零依赖)**,沿用核心模块的既有选择(不引入 vitest)。测试位于 `src/**/*.test.ts` 与 `tests/**/*.test.ts`。
|
|
165
|
+
|
|
166
|
+
覆盖矩阵(规范 §33 + 验收场景 A-G):
|
|
167
|
+
|
|
168
|
+
| 组 | 覆盖 |
|
|
169
|
+
|---|---|
|
|
170
|
+
| Export | 正常 / 空配置 / 大配置(1MB+) / Unicode / 特殊字符 / Secret 过滤 |
|
|
171
|
+
| Import | 正常 / Merge / Replace / Skip(不删目标独有 §32)/ Conflict / Missing plugin / Missing dependency / Missing secret / 未确认拒绝 |
|
|
172
|
+
| Rollback(场景 E) | 多 adapter 混合中途失败 → 整体恢复(settings / 文件 blob / workspace / patch 行);`rollbackOnError=false` 对照;部分回滚诚实报告 |
|
|
173
|
+
| Migration(场景 G) | `migrateToCurrent` 机制级:同版本 / 过新 / 低于最低 / 无路径 / 注册重叠 / 链式推进(**如实说明:当前 v1 即最新,无真实 v2 可端到端验证**) |
|
|
174
|
+
| Security(场景 F) | 畸形 ZIP / 超大条目数 / checksum 不匹配与缺失 / Zip Slip / 绝对路径 |
|
|
175
|
+
| Cross-platform(场景 B) | win32→darwin / darwin→win32 / linux→win32 批量前缀映射 |
|
|
176
|
+
| Redaction | 日志消息 / meta / 全链路不泄 Secret 值 |
|
|
177
|
+
| Schema | manifest 结构校验 / 版本判定函数 |
|
|
178
|
+
|
|
179
|
+
当前测试结果:**186 tests, all passing**(`npm test`),`npm run typecheck` 与 `npm run build` 均通过。
|
|
180
|
+
|
|
181
|
+
## Known limitations
|
|
182
|
+
|
|
183
|
+
1. **Workspace 只能建/改标题**:DSH 的 workspace 服务没有「整体覆盖」写通道——导入时可创建 workspace 与更新标题;path 与会话列表由 DSH 依真实目录自行维护,跨设备路径通过路径映射适配。
|
|
184
|
+
2. **部分 DSH 核心包未发布公共 npm registry**(如 `@deepseek-ai/dsh-plugin-marketplace`、`dsh-host-plugin-inventory`):依赖其 API 的功能只在本地 profile 环境可用;安装本插件需跳过 peer 自动安装(见 [Installation](#installation) 的 `--legacy-peer-deps` 说明)。
|
|
185
|
+
3. **MCP 无管理 API**(研究报告 §4.3):MCP 以组合 patch 行导入,需重启 DSH 生效;无增删改 API。
|
|
186
|
+
4. **插件安装需重启**:`pluginMarketplace.installPlugin` 只返回 `needsRestart`,重启依赖 DSH Desktop。
|
|
187
|
+
5. **浏览器 localStorage UI 状态不迁移**(任务看板数据、面板宽度等):Host 无通道。
|
|
188
|
+
6. **keybindings / workflows 配置 / commands / rules 文件**:DSH 当前无这些概念,不实现分区(不发明)。
|
|
189
|
+
7. **凭据值无法回滚**:DSH 不回读凭据值,导入中覆盖的凭据在回滚时只能标记 `manualHint` 人工补录。
|
|
190
|
+
8. **新建项无法删除回滚**:DSH settings 无删除语义,导入新建的 namespace 在回滚时只能人工处理(如实报告 partial)。
|
|
191
|
+
9. **Schema 迁移**:v1→v2 为占位(当前 `CURRENT_SCHEMA_VERSION=1`),机制已就绪但无真实 v2 可验证。
|
|
192
|
+
10. **历史会话迁移**:默认关闭,v1 仅文件级复制。
|
|
193
|
+
11. **加密备份**:依赖用户设置强密码;密码丢失则 `secrets.enc` 无法解密(设计使然)。
|
|
194
|
+
|
|
195
|
+
## Manual Test(最短人工测试流程)
|
|
196
|
+
|
|
197
|
+
> 前提:两台 DSH(或同一台的两份配置目录);本插件已按 [Installation](#installation) 打包并安装。
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
DSH A
|
|
201
|
+
→ 打开 Config Manager → Export Configuration
|
|
202
|
+
→ 选择 Quick Export → 导出 dsh-config-<date>.zip(确认报告中 Secret 均被排除)
|
|
203
|
+
→ 将 ZIP 复制到 DSH B
|
|
204
|
+
|
|
205
|
+
DSH B
|
|
206
|
+
→ 打开 Config Manager → Import Configuration
|
|
207
|
+
→ 选择 ZIP → 等待 Analyzing... → 查看 Import Preview(分区/插件/路径映射/凭据补录清单)
|
|
208
|
+
→ 如有路径问题 → 选择映射目录(批量前缀映射)
|
|
209
|
+
→ 解决冲突(Keep Current / Use Imported / Review)
|
|
210
|
+
→ 确认 Import → 观察进度 → 查看结果报告
|
|
211
|
+
→ 补充缺失凭据(N credentials need attention)
|
|
212
|
+
→ Verify:确认 settings / 插件 / MCP / Prompts / Skills / Workspaces 已恢复;
|
|
213
|
+
若导入中途失败 → 确认已自动回滚、原配置可正常使用
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
**产品原则**:宁可少迁移一个配置,也不要破坏用户现有配置。任何 Import 都遵循 `Analyze → Preview → Backup → Modify → Validate → Rollback`;任何 Secret 都遵循 `不默认导出 / 不记日志 / 不暴露 / 不静默转移`。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# dsh-config-manager bundle patch: inserts the dual-face plugin row into the
|
|
2
|
+
# web profile roster. Applied as a profile bundle layer (the `dsh.bundle.patch`
|
|
3
|
+
# manifest field in package.json) over dsh-base; activate with
|
|
4
|
+
# dsh plugin --profile <name> add dsh-config-manager # published
|
|
5
|
+
# dsh plugin --profile <name> add link:<abs-or-~-path> # local dev
|
|
6
|
+
# (the CLI forwards to pnpm inside the profile directory).
|
|
7
|
+
#
|
|
8
|
+
# CLI hard criterion (verified from `dsh plugin add` source): a package is
|
|
9
|
+
# appended to the target profile's `dsh.profile.bundles` only when its
|
|
10
|
+
# manifest carries `dsh.bundle.patch` (package.json does — it points at this
|
|
11
|
+
# file); without that field the CLI treats the package as a plain dependency
|
|
12
|
+
# and warns. So this file plus the package.json `dsh.bundle.patch` key are
|
|
13
|
+
# what make dsh-config-manager a real bundle.
|
|
14
|
+
#
|
|
15
|
+
# Format mirrors @linxin666/dsh-ssh/cordis.patch.yml exactly (verified
|
|
16
|
+
# against the installed dsh-ssh@0.1.12): a bare plugin row by package name —
|
|
17
|
+
# the node half (exports ".") runs in the host process (backup/export/import
|
|
18
|
+
# engine, /api/dsh-config-manager routes, agent tools), and the `dsh.client`
|
|
19
|
+
# declaration in package.json makes the browser half (exports "./client")
|
|
20
|
+
# load in the web GUI.
|
|
21
|
+
#
|
|
22
|
+
# Row id must match the Host plugin's `export const name` in src/index.ts
|
|
23
|
+
# (dsh-ssh precedent: patch row id "ssh" == export const name "ssh").
|
|
24
|
+
# ASSUMED here: "config-manager" — align when the Host half lands.
|
|
25
|
+
#
|
|
26
|
+
# Host services this plugin consumes — real DSH package names from the
|
|
27
|
+
# architecture research report (Docs/research/dsh-architecture.md §1.4),
|
|
28
|
+
# each listed as a peerDependency in package.json; plugin code declares them
|
|
29
|
+
# via `inject` in src/index.ts:
|
|
30
|
+
# ctx.settings -> @deepseek-ai/dsh-settings (+ dsh-settings-file)
|
|
31
|
+
# ctx.credentials -> @deepseek-ai/dsh-credentials (+ dsh-credentials-local)
|
|
32
|
+
# ctx.llm -> @deepseek-ai/dsh-llm
|
|
33
|
+
# ctx.pluginInventory -> @deepseek-ai/dsh-host-plugin-inventory
|
|
34
|
+
# ctx.pluginMarketplace -> @deepseek-ai/dsh-plugin-marketplace (web profile)
|
|
35
|
+
# ctx.tools -> @deepseek-ai/dsh-tools
|
|
36
|
+
# ctx.webServer -> @deepseek-ai/dsh-host-webserver
|
|
37
|
+
# ctx.systemPrompt -> @deepseek-ai/dsh-system-prompt
|
|
38
|
+
# ctx.agentPresets -> @deepseek-ai/dsh-agent-presets
|
|
39
|
+
# Settings namespaces read via ctx.settings.describe() — registered by these
|
|
40
|
+
# provider plugins (report §2.2), not services:
|
|
41
|
+
# llm-deepseek (settings.yaml section) -> @deepseek-ai/dsh-llm-deepseek
|
|
42
|
+
# llm-pi-ai (settings.yaml section) -> @deepseek-ai/dsh-llm-pi-ai
|
|
43
|
+
- insert:
|
|
44
|
+
- id: config-manager
|
|
45
|
+
name: 'dsh-config-manager'
|
package/dsh.bundle.patch
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# dsh-config-manager bundle patch — standalone copy under the task-mandated
|
|
2
|
+
# filename `dsh.bundle.patch`.
|
|
3
|
+
#
|
|
4
|
+
# NOTE on the real reference (@linxin666/dsh-ssh@0.1.12, verified): there is
|
|
5
|
+
# NO file named `dsh.bundle.patch` in the wild. `dsh.bundle.patch` is the JSON
|
|
6
|
+
# key inside package.json —
|
|
7
|
+
# "dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
|
|
8
|
+
# — that points at the bundle patch FILE, which dsh-ssh names cordis.patch.yml.
|
|
9
|
+
# This file is an equivalent duplicate for consumers expecting the name
|
|
10
|
+
# `dsh.bundle.patch`. package.json currently points at ./cordis.patch.yml
|
|
11
|
+
# (exactly like dsh-ssh). If you switch the pointer, update package.json:
|
|
12
|
+
# "dsh": { "bundle": { "patch": "./dsh.bundle.patch" } }
|
|
13
|
+
# Reference only ONE of the two files at a time — the loader applies each
|
|
14
|
+
# bundle's patch file once per bundle layer.
|
|
15
|
+
#
|
|
16
|
+
# Content is identical to ./cordis.patch.yml (same row, same id semantics:
|
|
17
|
+
# row id must match the Host plugin's `export const name` in src/index.ts;
|
|
18
|
+
# ASSUMED "config-manager" until the Host half lands).
|
|
19
|
+
- insert:
|
|
20
|
+
- id: config-manager
|
|
21
|
+
name: 'dsh-config-manager'
|
package/dsh.client
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// dsh-config-manager — client-half manifest (task-mandated standalone file;
|
|
2
|
+
// it mirrors the inline `dsh.client` block in package.json and adds the
|
|
3
|
+
// entry/mount declarations for the parallel src/client implementation).
|
|
4
|
+
//
|
|
5
|
+
// NOTE on the real reference (@linxin666/dsh-ssh@0.1.12, verified): the
|
|
6
|
+
// client half is declared inline in package.json as
|
|
7
|
+
// "dsh": { "client": { "inject": [...], "platform": "web" } }
|
|
8
|
+
// and the browser bundle is served at /plugins/<id>/client.js through the
|
|
9
|
+
// `./client` export. There is no standalone dsh.client file in the wild.
|
|
10
|
+
// Keep this file and the package.json block in sync.
|
|
11
|
+
{
|
|
12
|
+
// Services the client bundle needs injected (identical to dsh-ssh's block).
|
|
13
|
+
"inject": [
|
|
14
|
+
"@deepseek-ai/dsh-client-runtime",
|
|
15
|
+
"@deepseek-ai/dsh-client-connection",
|
|
16
|
+
"@deepseek-ai/dsh-client-ui-settings"
|
|
17
|
+
],
|
|
18
|
+
"platform": "web",
|
|
19
|
+
"entry": {
|
|
20
|
+
// VERIFIED (p4, 2026-08): the actual build output matches the dsh-ssh
|
|
21
|
+
// shape — tsdown produces the single-file browser bundle lib/client.js
|
|
22
|
+
// (starting with `window.__ModuleLoader__.load({ id, factory(require) })`),
|
|
23
|
+
// served through the "./client" export in package.json:
|
|
24
|
+
// "exports": { "./client": { "types": "./lib/client.d.ts",
|
|
25
|
+
// "default": "./lib/client.js" } }
|
|
26
|
+
// Source is the p1 client entry (src/client/index.ts): inject + apply
|
|
27
|
+
// registering the `settings.section` page (dsh-ssh src/client precedent).
|
|
28
|
+
"source": "src/client/index.ts",
|
|
29
|
+
"build": "lib/client.js"
|
|
30
|
+
},
|
|
31
|
+
"mount": {
|
|
32
|
+
// Official Slot contract (research report §1.5, design doc §11):
|
|
33
|
+
// settings.section registers a settings page. Design doc §11.4 picks it
|
|
34
|
+
// as the Config Manager main surface.
|
|
35
|
+
"slot": "settings.section",
|
|
36
|
+
"id": "config-manager",
|
|
37
|
+
"label": "Backup & Migration"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentPresets 分区 adapter(设计 §3.3):
|
|
3
|
+
* 数据源 = ~/.dsh/.agent-presets/(用户可写目录,每预设一个目录:agent.cordis.yml + preset.yml)。
|
|
4
|
+
* system 预设(安装目录)只记引用不复制(本 adapter 只读用户目录)。
|
|
5
|
+
*/
|
|
6
|
+
import { FileCollectionAdapter } from './file-collection.ts';
|
|
7
|
+
export declare class AgentPresetsAdapter extends FileCollectionAdapter {
|
|
8
|
+
readonly id: "agentPresets";
|
|
9
|
+
readonly displayName = "Agent Presets";
|
|
10
|
+
readonly defaultIncluded = true;
|
|
11
|
+
readonly portability: "portable";
|
|
12
|
+
readonly baseDir = ".agent-presets";
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agentPresets 分区 adapter(设计 §3.3):
|
|
3
|
+
* 数据源 = ~/.dsh/.agent-presets/(用户可写目录,每预设一个目录:agent.cordis.yml + preset.yml)。
|
|
4
|
+
* system 预设(安装目录)只记引用不复制(本 adapter 只读用户目录)。
|
|
5
|
+
*/
|
|
6
|
+
import { FileCollectionAdapter } from "./file-collection.js";
|
|
7
|
+
export class AgentPresetsAdapter extends FileCollectionAdapter {
|
|
8
|
+
id = 'agentPresets';
|
|
9
|
+
displayName = 'Agent Presets';
|
|
10
|
+
defaultIncluded = true;
|
|
11
|
+
portability = 'portable';
|
|
12
|
+
baseDir = '.agent-presets';
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=agent-presets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-presets.js","sourceRoot":"","sources":["../../src/adapters/agent-presets.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,mBAAoB,SAAQ,qBAAqB;IACnD,EAAE,GAAG,cAAuB,CAAC;IAC7B,WAAW,GAAG,eAAe,CAAC;IAC9B,eAAe,GAAG,IAAI,CAAC;IACvB,WAAW,GAAG,UAAmB,CAAC;IAClC,OAAO,GAAG,gBAAgB,CAAC;CACrC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* credentialsStatus 分区 adapter(设计 §3.3/§7.1):
|
|
3
|
+
* 数据源 = ctx.credentials.describe(ref) 的状态(configured/source/writable),
|
|
4
|
+
* 以及 settings secrets 标记 / llm apiKeyEnv 中引用的凭据 ref 名。
|
|
5
|
+
*
|
|
6
|
+
* 安全不变量:永不导出值(hasValue 恒 false);导入生成 MissingSecret 清单,
|
|
7
|
+
* 用户补录值经 ctx.secretInputs / decryptedCredentials(仅内存)→ credentials.set()。
|
|
8
|
+
* .credentials.yaml 文件字节交由 m4 加密层处理,本 adapter 不触碰。
|
|
9
|
+
*/
|
|
10
|
+
import type { CredentialsSection } from '../schema/types.ts';
|
|
11
|
+
import type { ApplyResult, ConfigAdapter, ExportOptions, ExportSection, HostContext, ImportContext, PlanItem, ValidationResult } from '../core/types.ts';
|
|
12
|
+
import { type NamespaceProvider } from './settings.ts';
|
|
13
|
+
export type CredentialRefsProvider = (ctx: HostContext) => Promise<string[]>;
|
|
14
|
+
/** 缺省 ref 收集:遍历 settings namespace,收集 llm apiKeyEnv / providers[].apiKeyEnv,
|
|
15
|
+
* 以及 secrets 标记中「引用类字段」(apiKeyEnv/tokenEnv…)的字段值。
|
|
16
|
+
* 注意 secrets[].path[0] 是 settings 文档内的字段路径(如 ['apiKey']),本身不是凭据 ref(设计 §4.2),
|
|
17
|
+
* 只有指向 env 名的引用字段才值得收集。 */
|
|
18
|
+
export declare function defaultCredentialRefs(namespaces: string[] | NamespaceProvider): CredentialRefsProvider;
|
|
19
|
+
export interface CredentialsAdapterOptions {
|
|
20
|
+
/** 凭据 ref 名收集器(缺省从 settings 推断) */
|
|
21
|
+
refs?: CredentialRefsProvider;
|
|
22
|
+
/** 供缺省 refs 使用的 namespace 清单 */
|
|
23
|
+
namespaces?: string[] | NamespaceProvider;
|
|
24
|
+
}
|
|
25
|
+
export declare class CredentialsAdapter implements ConfigAdapter<CredentialsSection> {
|
|
26
|
+
readonly id: "credentialsStatus";
|
|
27
|
+
readonly displayName = "Credentials";
|
|
28
|
+
readonly defaultIncluded = true;
|
|
29
|
+
readonly portability: "deviceSpecific";
|
|
30
|
+
private readonly refs;
|
|
31
|
+
constructor(options?: CredentialsAdapterOptions);
|
|
32
|
+
export(ctx: HostContext, _options: ExportOptions): Promise<ExportSection<CredentialsSection>>;
|
|
33
|
+
analyzeImport(_data: CredentialsSection, _ctx: ImportContext): Promise<PlanItem[]>;
|
|
34
|
+
applyItem(item: PlanItem, ctx: ImportContext): Promise<ApplyResult>;
|
|
35
|
+
validate(data: CredentialsSection): Promise<ValidationResult>;
|
|
36
|
+
}
|