dsh-config-manager 0.2.2 → 0.2.3

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.
@@ -53,7 +53,7 @@ export const zh = {
53
53
  'core.journal.notTerminal': 'moveToCompleted 仅接受 terminal journal: {opId} state={state}',
54
54
  // ---------- run-registry ----------
55
55
  'core.run.autosyncBusy': '自动同步已在进行中',
56
- 'core.run.syncApplyBusy': '一键同步已在进行中',
56
+ 'core.run.syncApplyBusy': '正在从远端合并',
57
57
  'core.run.backupScheduleBusy': '定时备份已在进行中',
58
58
  'core.run.restoreBusy': '快照恢复已在进行中(请等待当前恢复完成)',
59
59
  'core.run.recoveryBusy': '恢复/回滚已在进行中(请等待当前恢复完成)',
@@ -349,7 +349,7 @@ export const en = {
349
349
  'core.journal.notTerminal': 'moveToCompleted only accepts a terminal journal: {opId} state={state}',
350
350
  // ---------- run-registry ----------
351
351
  'core.run.autosyncBusy': 'Automatic sync is already in progress',
352
- 'core.run.syncApplyBusy': 'One-click sync is already in progress',
352
+ 'core.run.syncApplyBusy': 'Merge from remote is already in progress',
353
353
  'core.run.backupScheduleBusy': 'Scheduled backup is already in progress',
354
354
  'core.run.restoreBusy': 'Snapshot restore is already in progress (wait for the current restore to finish)',
355
355
  'core.run.recoveryBusy': 'Recovery/rollback is already in progress (wait for the current recovery to finish)',
package/lib/index.d.ts CHANGED
@@ -77,7 +77,7 @@ export declare const name = "config-manager";
77
77
  /** Services required before the engine can mount (present in every profile). */
78
78
  export declare const inject: string[];
79
79
  /** Plugin version, kept in sync with package.json ("version"). */
80
- export declare const PLUGIN_VERSION = "0.2.2";
80
+ export declare const PLUGIN_VERSION = "0.2.3";
81
81
  /**
82
82
  * Star 引导弹窗指向的 GitHub 仓库(用户引导点 Star 的目标)。
83
83
  * 与 package.json 的 repository 字段保持一致;界面不可改(硬编码,参照
package/lib/index.js CHANGED
@@ -136,7 +136,7 @@ export const name = 'config-manager';
136
136
  /** Services required before the engine can mount (present in every profile). */
137
137
  export const inject = ['settings', 'credentials'];
138
138
  /** Plugin version, kept in sync with package.json ("version"). */
139
- export const PLUGIN_VERSION = '0.2.2';
139
+ export const PLUGIN_VERSION = '0.2.3';
140
140
  /** Plugin own package name — excluded from its own exported plugins list. */
141
141
  const PLUGIN_NAME = 'dsh-config-manager';
142
142
  /**
@@ -21,13 +21,13 @@ export declare const zh: {
21
21
  readonly 'route.syncPreviewFailed': '同步预览失败';
22
22
  readonly 'route.syncSessionExpired': '同步会话不存在或已过期,请重新拉取预览';
23
23
  readonly 'route.syncConflictResolutionRequired': '冲突项 {id} 必须提供 resolution(useRemote/keepLocal/skip)';
24
- readonly 'route.syncApplySummary': '一键同步应用:{count} 项{status}{ineffective}';
24
+ readonly 'route.syncApplySummary': '从远端合并:{count} 项{status}{ineffective}';
25
25
  readonly 'route.syncRollbackIncomplete': '(回滚未完成)';
26
26
  readonly 'route.syncRolledBack': '(已回滚)';
27
27
  readonly 'route.syncIneffectiveSuffix': '({count} 项未生效)';
28
28
  readonly 'route.syncApplyPartial': '同步应用部分成功:有项未生效(见告警明细)';
29
29
  readonly 'route.syncApplyFailed': '同步应用未完全成功';
30
- readonly 'route.syncRollbackSummary': '一键同步回滚({id})';
30
+ readonly 'route.syncRollbackSummary': '从远端合并回滚({id})';
31
31
  readonly 'route.recoverySessionRepairRollback': '会话修复回滚';
32
32
  readonly 'route.recoverySessionRepairDropped': '会话修复(丢弃重放重复行 {count} 行)';
33
33
  readonly 'route.recoverySessionRepairRejected': '会话修复被拒绝:{reason}';
@@ -39,13 +39,13 @@ export const zh = {
39
39
  'route.syncPreviewFailed': '同步预览失败',
40
40
  'route.syncSessionExpired': '同步会话不存在或已过期,请重新拉取预览',
41
41
  'route.syncConflictResolutionRequired': '冲突项 {id} 必须提供 resolution(useRemote/keepLocal/skip)',
42
- 'route.syncApplySummary': '一键同步应用:{count} 项{status}{ineffective}',
42
+ 'route.syncApplySummary': '从远端合并:{count} 项{status}{ineffective}',
43
43
  'route.syncRollbackIncomplete': '(回滚未完成)',
44
44
  'route.syncRolledBack': '(已回滚)',
45
45
  'route.syncIneffectiveSuffix': '({count} 项未生效)',
46
46
  'route.syncApplyPartial': '同步应用部分成功:有项未生效(见告警明细)',
47
47
  'route.syncApplyFailed': '同步应用未完全成功',
48
- 'route.syncRollbackSummary': '一键同步回滚({id})',
48
+ 'route.syncRollbackSummary': '从远端合并回滚({id})',
49
49
  // ---------- 事故恢复 ----------
50
50
  'route.recoverySessionRepairRollback': '会话修复回滚',
51
51
  'route.recoverySessionRepairDropped': '会话修复(丢弃重放重复行 {count} 行)',
@@ -79,13 +79,13 @@ export const en = {
79
79
  'route.syncPreviewFailed': 'Sync preview failed',
80
80
  'route.syncSessionExpired': 'The sync session does not exist or has expired; pull a fresh preview',
81
81
  'route.syncConflictResolutionRequired': 'Conflict item {id} must provide a resolution (useRemote/keepLocal/skip)',
82
- 'route.syncApplySummary': 'One-click sync apply: {count} item(s){status}{ineffective}',
82
+ 'route.syncApplySummary': 'Merge from remote: {count} item(s){status}{ineffective}',
83
83
  'route.syncRollbackIncomplete': ' (rollback incomplete)',
84
84
  'route.syncRolledBack': ' (rolled back)',
85
85
  'route.syncIneffectiveSuffix': ' ({count} item(s) had no effect)',
86
86
  'route.syncApplyPartial': 'Sync apply partially succeeded: some items had no effect (see the warning details)',
87
87
  'route.syncApplyFailed': 'Sync apply did not fully succeed',
88
- 'route.syncRollbackSummary': 'One-click sync rollback ({id})',
88
+ 'route.syncRollbackSummary': 'Merge from remote rollback ({id})',
89
89
  'route.recoverySessionRepairRollback': 'Session repair rollback',
90
90
  'route.recoverySessionRepairDropped': 'Session repair (dropped {count} replayed duplicate row(s))',
91
91
  'route.recoverySessionRepairRejected': 'Session repair was rejected: {reason}',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-config-manager",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "license": "MIT",
5
5
  "description": "Backup, restore, export, import, migrate and sync your DeepSeek Harness (DSH) configuration: settings, plugins, MCP servers, skills, agent presets and workspaces. One-click migration to another machine. DSH 配置一键备份、恢复、导出、导入、迁移与同步插件。",
6
6
  "icon": "./icon.svg",
@@ -71,7 +71,7 @@ export const zh = {
71
71
  'history.kind.profile-rename': '档案重命名',
72
72
  'history.kind.profile-save': '档案保存',
73
73
  'history.kind.profile-import': '档案导入',
74
- 'history.kind.sync-apply': '一键同步应用',
74
+ 'history.kind.sync-apply': '从远端合并',
75
75
  'history.kind.autosync': '自动同步',
76
76
  'history.kind.recovery': '恢复/回滚编排',
77
77
  'history.kind.backup': '定时备份',
@@ -118,7 +118,7 @@ export const en = {
118
118
  'history.kind.profile-rename': 'Profile rename',
119
119
  'history.kind.profile-save': 'Profile save',
120
120
  'history.kind.profile-import': 'Profile import',
121
- 'history.kind.sync-apply': 'Sync apply',
121
+ 'history.kind.sync-apply': 'Merge from remote',
122
122
  'history.kind.autosync': 'Autosync',
123
123
  'history.kind.recovery': 'Recovery/rollback',
124
124
  'history.kind.backup': 'Scheduled backup',
@@ -123,7 +123,7 @@ export const zh = {
123
123
  'overview.kind.other': '其他操作',
124
124
  // 状态横幅
125
125
  // —— 导出 ——
126
- 'export.hint': '默认导出推荐分区(可迁移、不含设备专属数据)。点「选择要导出的内容」可按分区、逐条目调整(技能、插件、工作区、会话等)。',
126
+ 'export.hint': '默认导出推荐分区(可迁移、不含设备专属数据)。',
127
127
  'export.encrypt': '加密备份',
128
128
  'export.encryptHint': '用密码加密整个备份文件(AES-256-GCM)。密码仅用于本次加密,不会写入备份或任何配置文件;导入时必须输入该密码才能解锁。',
129
129
  'export.password': '备份加密密码',
@@ -640,7 +640,7 @@ export const zh = {
640
640
  'runs.kind.export': '导出',
641
641
  'runs.kind.import': '导入',
642
642
  'runs.kind.autosync': '自动同步',
643
- 'runs.kind.sync-apply': '一键同步',
643
+ 'runs.kind.sync-apply': '从远端合并',
644
644
  'runs.kind.backup-schedule': '定时备份',
645
645
  'runs.kind.restore': '快照恢复',
646
646
  'runs.kind.profile-switch': '档案切换',
@@ -1013,7 +1013,7 @@ export const en: Record<keyof typeof zh, string> = {
1013
1013
  'overview.kind.snapshot-delete': 'Snapshot delete',
1014
1014
  'overview.kind.snapshot-prune': 'Snapshot prune',
1015
1015
  'overview.kind.other': 'Other operation',
1016
- 'export.hint': 'Exports the recommended sections by default (portable, no device-specific data). Click "Choose what to export" to adjust per section and per item (skills, plugins, workspaces, sessions, …).',
1016
+ 'export.hint': 'Exports the recommended sections by default (portable, no device-specific data).',
1017
1017
  'export.encrypt': 'Encrypt backup',
1018
1018
  'export.encryptHint': 'Encrypt the entire backup file with a password (AES-256-GCM). The password is used once and never written into the backup or any config file; it is required to unlock the backup on import.',
1019
1019
  'export.password': 'Backup encryption password',
@@ -1518,7 +1518,7 @@ export const en: Record<keyof typeof zh, string> = {
1518
1518
  'runs.kind.export': 'Export',
1519
1519
  'runs.kind.import': 'Import',
1520
1520
  'runs.kind.autosync': 'Auto sync',
1521
- 'runs.kind.sync-apply': 'One-click sync',
1521
+ 'runs.kind.sync-apply': 'Merge from remote',
1522
1522
  'runs.kind.backup-schedule': 'Scheduled backup',
1523
1523
  'runs.kind.restore': 'Snapshot restore',
1524
1524
  'runs.kind.profile-switch': 'Profile switch',
@@ -35,7 +35,6 @@ import type { MyConfigsApi, MyItemEntry } from './my-configs-api.ts'
35
35
  import type { ListingStatusResponse } from '../../market/my-repo.ts'
36
36
  import type { SyncApi, GithubPollResponse } from '../sync/sync-api.ts'
37
37
  import { Button, Card, SectionTitle } from '../common/ui.tsx'
38
- import { InfoHint } from '../common/InfoHint.tsx'
39
38
  import { ConfirmDialog } from '../common/ConfirmDialog.tsx'
40
39
  import { toast } from '../common/toast-store.ts'
41
40
  import { redact } from '../../security/redaction.ts'
@@ -773,10 +772,11 @@ export function MyConfigsView({
773
772
  <>
774
773
  {/* 一键上传入口:点按钮 → 免责(首次)→ 弹窗向导 */}
775
774
  <Card>
776
- {/* 卡片里只有一个动作:按钮本身已经写明「一键上传」,不再重复一个同名标题;ⓘ 跟着动作走 */}
775
+ {/* 卡片里只有一个动作:按钮本身已写明作用对象(上传配置包),不再重复标题。
776
+ 此处不再放 ⓘ:它的「先在导出页生成 zip / 含密钥的备份会被拒绝」已在点开后的
777
+ 向导头(MyConfigsWizard 的 Modal.Header trailing)逐字出现,入口再放一遍是同一条信息两次。 */}
777
778
  <div className={css.actionRow}>
778
779
  <Button variant="primary" onClick={openUpload}>{t('myconfigs.upload.run')}</Button>
779
- <InfoHint text={t('myconfigs.upload.selectHint')} label={t('common.infoHint')} />
780
780
  </div>
781
781
  </Card>
782
782
  {renderList()}
@@ -145,11 +145,11 @@ export const zh = {
145
145
  'myconfigs.upload.mode.migrate': '迁移(保留全部内容)',
146
146
  'myconfigs.upload.mode.share': '分享(自动排除敏感内容)',
147
147
  'myconfigs.upload.mode.shareHint': '分享模式将自动排除设备/平台相关分区(凭据状态 / 会话 / MCP / 工作区等),并对隐私内容强制拦截——发现任何敏感痕迹将拒绝发布,而不是仅警告。',
148
- 'myconfigs.upload.run': '一键上传',
148
+ 'myconfigs.upload.run': '上传配置包',
149
149
  'myconfigs.update.title': '更新配置',
150
150
  'myconfigs.update.hint': '版本自动 +1',
151
151
  'myconfigs.update.run': '一键更新',
152
- 'myconfigs.update.zipHint': '更新需要新的配置包:选择新 zip 后自动校验,通过后即可一键更新。',
152
+ 'myconfigs.update.zipHint': '选择新 zip 后自动校验,通过才能更新。',
153
153
  'myconfigs.update.selectZip': '选择新 ZIP…',
154
154
  // 上传/更新结果卡
155
155
  'myconfigs.result.title': '上传/更新完成',
@@ -324,11 +324,11 @@ export const en: Record<keyof typeof zh, string> = {
324
324
  'myconfigs.upload.mode.migrate': 'Migrate (keep everything)',
325
325
  'myconfigs.upload.mode.share': 'Share (auto-exclude sensitive content)',
326
326
  'myconfigs.upload.mode.shareHint': 'Share mode auto-excludes device/platform-specific sections (credentials status / sessions / MCP / workspaces, etc.) and enforces privacy scanning — any sensitive trace blocks publishing instead of just warning.',
327
- 'myconfigs.upload.run': 'Upload',
327
+ 'myconfigs.upload.run': 'Upload bundle',
328
328
  'myconfigs.update.title': 'Update config',
329
329
  'myconfigs.update.hint': 'version +1 automatically',
330
330
  'myconfigs.update.run': 'Update',
331
- 'myconfigs.update.zipHint': 'Updating needs a new config zip: pick one and it will be validated automatically, then you can update.',
331
+ 'myconfigs.update.zipHint': 'Pick a new zip and it will be validated automatically; only a passing zip can be installed.',
332
332
  'myconfigs.update.selectZip': 'Choose new ZIP…',
333
333
  'myconfigs.result.title': 'Upload/update complete',
334
334
  'myconfigs.result.version': 'version {version}',
@@ -1260,8 +1260,8 @@ export function SyncPanel({ api, t, cmT }: SyncPanelProps) {
1260
1260
  )}
1261
1261
  {ready && (
1262
1262
  <Card>
1263
- {/* 动作行(方案 A 定案):用户只面对两个方向 —— 下行「一键同步」/ 上行「推送到远端」。
1264
- 合并裁决并入一键同步(有基线走三方裁决视图、无基线走老导入确认);并排两个按钮看不出该按哪个(issue 115)⇒ 按已确证远端事实只让一个落在 primary。 */}
1263
+ {/* 动作行(方案 A 定案):用户只面对两个方向 —— 下行「从远端合并到本机」/ 上行「推送到远端」。
1264
+ 合并裁决并入下行按钮(有基线走三方裁决视图、无基线走老导入确认);并排两个按钮看不出该按哪个(issue 115)⇒ 按已确证远端事实只让一个落在 primary。 */}
1265
1265
  <div className={css.actionRow}>
1266
1266
  <Button
1267
1267
  variant={pickSyncPrimary(state.statusInfo, channel) === 'sync' ? 'primary' : 'ghost'}
@@ -111,9 +111,10 @@ export function SyncProbeRow({ api, t, body, remoteReady, busy, presentation = '
111
111
 
112
112
  return (
113
113
  <>
114
- {/* action 形态:直接摊进调用方自己的动作行(不再套一层 .actionRow),结果走 toast */}
114
+ {/* action 形态:直接摊进调用方自己的动作行(不再套一层 .actionRow),结果走 toast。
115
+ 动作行里只留按钮:与同排其它按钮并排时,ⓘ 会被读成该按钮的开关(issue 121 用户要求移除)。 */}
115
116
  {actionMode
116
- ? <>{button}{hintNode}</>
117
+ ? <>{button}</>
117
118
  : <div className={css.actionRow}>{button}{hintNode}</div>}
118
119
  {!actionMode && error !== null && (
119
120
  <ErrorBanner
@@ -20,8 +20,8 @@ export const zh = {
20
20
  'config.tokenMissing': '未配置令牌:可拉取公开仓库,无法推送',
21
21
  'config.tokenPlaceholder': 'ghp_…(可选)',
22
22
  // 配置保存(自动保存 + 显式保存按钮)
23
- 'config.save': '保存配置',
24
- 'config.saveHint': '表单改动会自动保存(密码/token 安全写入 DSH credentials,不会写入同步文件或日志);也可点击按钮立即保存。',
23
+ 'config.save': '立即保存',
24
+ 'config.saveHint': '表单改动会自动保存:密码/token 安全写入 DSH credentials,不会写入同步文件或日志。',
25
25
  // 通道选择器
26
26
  'channel.title': '同步通道',
27
27
  'channel.git': 'Git 私有仓库',
@@ -31,7 +31,7 @@ export const zh = {
31
31
  'channel.perChannelHint': '每条通道(Git / WebDAV / 对象存储 / Gist)的自动同步、同步模式、加密与远端快照各自独立配置。',
32
32
  // 通道入口卡(弹窗驱动:点按钮 → 弹窗内配置通道)
33
33
  'channel.open': '配置…',
34
- 'channel.openHint': '远程同步通过「同步通道」进行:Git 私有仓库或 WebDAV 服务器。点击按钮在弹窗中配置或修改。',
34
+ 'channel.openHint': '远程同步通过「同步通道」进行:Git 私有仓库或 WebDAV 服务器。',
35
35
  // 断开通道配置(用户实测要求:配置过就必须能删掉,否则打不通的通道会永久占位)
36
36
  'channel.expand': '展开通道详情',
37
37
  'channel.collapse': '收起通道详情',
@@ -125,9 +125,11 @@ export const zh = {
125
125
  'push.title': '推送结果',
126
126
  'sections.title': '同步分区',
127
127
  'warnings.title': '分区告警',
128
- // 一键同步(方案 A:拉取 → 逐项确认 → apply-items)
129
- 'syncflow.title': '一键同步',
130
- 'syncflow.button': '一键同步',
128
+ // 从远端合并到本机(方案 A:拉取 → 逐项确认 → apply-items)
129
+ // 名字必须写明方向(issue 121):这条链路只下行、绝不上传 —— 叫「同步」会被读成双向。
130
+ 'syncflow.title': '从远端合并到本机',
131
+ 'syncflow.button': '从远端合并到本机',
132
+ // 方向说明(ⓘ,挂在动作行):只下行 + 双向该去哪儿;常驻成行会与按钮名重复(DESIGN.md §7)
131
133
  // 方案 A:有基线且三方合并全部 skip = 远端与本机一致,不必同步
132
134
  'syncflow.upToDate': '已是最新,无需同步',
133
135
  'syncflow.syncing': '正在同步…',
@@ -177,7 +179,7 @@ export const zh = {
177
179
  'syncflow.remoteEmptyGuide': '远端还没有任何快照:这是首次同步,先点「推送到远端」把本机配置推上去(推送会创建第一个快照),之后才能拉取。',
178
180
  // 同步模式(默认快速导出 / 高级自定义导出)
179
181
  'mode.title': '同步分区',
180
- 'mode.hint': '点按钮在弹窗里勾选要同步的分区:勾选集合就是同步范围(默认已勾选推荐分区,可随时改)。',
182
+ 'mode.hint': '同步范围 = 勾选的分区集合(默认已勾选推荐分区,可随时改)。',
181
183
  'mode.selectedCount': '已勾选 {n} 个',
182
184
  'mode.sectionsHint': '默认可同步的是可移植分区;平台相关分区(MCP 服务器)与设备相关分区(插件文件、凭据状态、历史会话)只有你在这里主动勾选才参与同步。',
183
185
  'mode.atLeastOne': '请至少勾选一个同步分区',
@@ -216,7 +218,7 @@ export const zh = {
216
218
  'mode.includeSecretsHint': '把真实凭据值(~/.dsh/.credentials.yaml)写入加密快照,可在另一台机器上随同步一并写回本机凭据;勾选时自动选中加密,密钥绝不进入未加密快照。',
217
219
  'mode.encryptAutosyncNotice': '加密快照仅通过手动推送/拉取使用;自动同步无密码,遇到加密快照会跳过并在历史中提示。',
218
220
  'mode.decryptPassword': '解密密码(加密快照拉取/同步用,可选)',
219
- 'mode.decryptPasswordHint': '输入后保存在本机凭据库,下次拉取自动使用;只有拉取的快照确实加密时才会用到 —— 未加密的备份不会使用密码。删除请点「删除已保存密码」。',
221
+ 'mode.decryptPasswordHint': '输入后保存在本机凭据库,下次拉取自动使用;只有拉取的快照确实加密时才会用到 —— 未加密的备份不会使用密码。',
220
222
  'mode.decryptPasswordSaved': '已保存到本机',
221
223
  'mode.decryptPasswordPlaceholder': '已保存(留空即沿用)',
222
224
  'mode.clearSavedPassword': '删除已保存密码',
@@ -257,7 +259,7 @@ export const zh = {
257
259
  // 同步历史(含自动同步记录)
258
260
  'history.title': '同步历史',
259
261
  'history.empty': '尚无同步历史',
260
- 'history.emptyHint': '完成首次 push / 一键同步 / 自动同步后,这里会显示记录。',
262
+ 'history.emptyHint': '完成首次推送 / 从远端合并 / 自动同步后,这里会显示记录。',
261
263
  'history.colTime': '时间',
262
264
  'history.colDetail': '详情',
263
265
  'history.kindAutosync': '自动同步',
@@ -304,7 +306,7 @@ export const zh = {
304
306
  'toast.pushPreviewFailed': '推送预览失败',
305
307
  'toast.pushFailed': '推送失败',
306
308
  'toast.pushDone': '推送完成',
307
- 'toast.syncStartFailed': '一键同步失败',
309
+ 'toast.syncStartFailed': '从远端合并失败',
308
310
  'toast.autosyncUpdateFailed': '自动同步设置更新失败',
309
311
  'toast.autosyncUpdated': '自动同步设置已更新',
310
312
  'toast.snapshotsLoadFailed': '读取远端快照失败',
@@ -474,8 +476,8 @@ export const en: Record<keyof typeof zh, string> = {
474
476
  'config.tokenMissing': 'No token: public repos can be pulled, but pushing will fail',
475
477
  'config.tokenPlaceholder': 'ghp_… (optional)',
476
478
  // Config save (auto-save + explicit save button)
477
- 'config.save': 'Save config',
478
- 'config.saveHint': 'Form changes are saved automatically (password/token written securely into DSH credentials, never into sync files or logs); you can also save explicitly with the button.',
479
+ 'config.save': 'Save now',
480
+ 'config.saveHint': 'Form changes are saved automatically: password/token goes securely into DSH credentials, never into sync files or logs.',
479
481
  'channel.title': 'Sync Channel',
480
482
  'channel.git': 'Git private repo',
481
483
  'channel.webdav': 'WebDAV server',
@@ -484,7 +486,7 @@ export const en: Record<keyof typeof zh, string> = {
484
486
  'channel.perChannelHint': 'Auto sync, sync mode, encryption and remote snapshots are configured independently for each channel (Git / WebDAV / object storage / Gist).',
485
487
  // Channel entry card (dialog-driven: click the button → configure the channel in the dialog)
486
488
  'channel.open': 'Configure…',
487
- 'channel.openHint': 'Remote sync runs through a sync channel: a private Git repository or a WebDAV server. Click the button to configure or change it in the dialog.',
489
+ 'channel.openHint': 'Remote sync runs through a sync channel: a private Git repository or a WebDAV server.',
488
490
  // Disconnect channel config (user report: once configured there was no way out)
489
491
  'channel.expand': 'Expand channel details',
490
492
  'channel.collapse': 'Collapse channel details',
@@ -573,8 +575,8 @@ export const en: Record<keyof typeof zh, string> = {
573
575
  'push.title': 'Push Result',
574
576
  'sections.title': 'Synced Sections',
575
577
  'warnings.title': 'Section Warnings',
576
- 'syncflow.title': 'One-Click Sync',
577
- 'syncflow.button': 'One-click sync',
578
+ 'syncflow.title': 'Merge from remote',
579
+ 'syncflow.button': 'Merge from remote',
578
580
  // Plan A: with a baseline, an all-skip three-way merge means local and remote already match
579
581
  'syncflow.upToDate': 'Already up to date; nothing to sync',
580
582
  'syncflow.syncing': 'Syncing…',
@@ -623,7 +625,7 @@ export const en: Record<keyof typeof zh, string> = {
623
625
  'syncflow.remoteEmptyGuide': 'No remote snapshots yet: this is the first sync — click "Push to remote" to upload your local config (that push creates the first snapshot); pulling only works afterwards.',
624
626
  // Sync mode (default quick export / advanced custom export)
625
627
  'mode.title': 'Sync Sections',
626
- 'mode.hint': 'Click the button to tick the sections to sync: the ticked set is the sync scope (recommended sections are ticked by default and can be changed any time).',
628
+ 'mode.hint': 'Sync scope = the ticked set of sections (recommended sections are ticked by default and can be changed any time).',
627
629
  'mode.selectedCount': '{n} selected',
628
630
  'mode.sectionsHint': 'Portable sections sync by default; platform-dependent sections (MCP servers) and device-specific sections (plugin files, credentials status, sessions) only participate when you explicitly tick them here.',
629
631
  'mode.atLeastOne': 'Select at least one section to sync',
@@ -662,7 +664,7 @@ export const en: Record<keyof typeof zh, string> = {
662
664
  'mode.includeSecretsHint': 'Write real credential values (~/.dsh/.credentials.yaml) into the encrypted snapshot so another machine can restore them with the sync; checking this auto-selects encryption, and secrets never enter a plaintext snapshot.',
663
665
  'mode.encryptAutosyncNotice': 'Encrypted snapshots are only produced/consumed by manual push/pull; auto sync has no password, so it skips encrypted snapshots and reports them in history.',
664
666
  'mode.decryptPassword': 'Decryption password (for encrypted snapshot pull/sync, optional)',
665
- 'mode.decryptPasswordHint': 'Once entered it is saved in the local credential store and reused automatically; it is only ever used when the pulled snapshot is actually encrypted — plaintext backups never use the password. Use "Delete saved password" to remove it.',
667
+ 'mode.decryptPasswordHint': 'Once entered it is saved in the local credential store and reused automatically; it is only ever used when the pulled snapshot is actually encrypted — plaintext backups never use the password.',
666
668
  'mode.decryptPasswordSaved': 'Saved locally',
667
669
  'mode.decryptPasswordPlaceholder': 'Saved (leave empty to reuse)',
668
670
  'mode.clearSavedPassword': 'Delete saved password',
@@ -701,7 +703,7 @@ export const en: Record<keyof typeof zh, string> = {
701
703
  'autosync.lastMessage': 'Last message',
702
704
  'history.title': 'Sync History',
703
705
  'history.empty': 'No sync history yet',
704
- 'history.emptyHint': 'Records appear here after your first push / one-click sync / auto sync.',
706
+ 'history.emptyHint': 'Records appear here after your first push / merge from remote / auto sync.',
705
707
  'history.colTime': 'Time',
706
708
  'history.colDetail': 'Detail',
707
709
  'history.kindAutosync': 'Auto Sync',
@@ -745,7 +747,7 @@ export const en: Record<keyof typeof zh, string> = {
745
747
  'toast.pushPreviewFailed': 'Failed to build push preview',
746
748
  'toast.pushFailed': 'Push failed',
747
749
  'toast.pushDone': 'Push completed',
748
- 'toast.syncStartFailed': 'One-click sync failed',
750
+ 'toast.syncStartFailed': 'Merge from remote failed',
749
751
  'toast.autosyncUpdateFailed': 'Failed to update auto-sync settings',
750
752
  'toast.autosyncUpdated': 'Auto-sync settings updated',
751
753
  'toast.snapshotsLoadFailed': 'Failed to load remote snapshots',
@@ -67,7 +67,7 @@ export const zh = {
67
67
 
68
68
  // ---------- run-registry ----------
69
69
  'core.run.autosyncBusy': '自动同步已在进行中',
70
- 'core.run.syncApplyBusy': '一键同步已在进行中',
70
+ 'core.run.syncApplyBusy': '正在从远端合并',
71
71
  'core.run.backupScheduleBusy': '定时备份已在进行中',
72
72
  'core.run.restoreBusy': '快照恢复已在进行中(请等待当前恢复完成)',
73
73
  'core.run.recoveryBusy': '恢复/回滚已在进行中(请等待当前恢复完成)',
@@ -381,7 +381,7 @@ export const en: Record<keyof typeof zh, string> = {
381
381
 
382
382
  // ---------- run-registry ----------
383
383
  'core.run.autosyncBusy': 'Automatic sync is already in progress',
384
- 'core.run.syncApplyBusy': 'One-click sync is already in progress',
384
+ 'core.run.syncApplyBusy': 'Merge from remote is already in progress',
385
385
  'core.run.backupScheduleBusy': 'Scheduled backup is already in progress',
386
386
  'core.run.restoreBusy': 'Snapshot restore is already in progress (wait for the current restore to finish)',
387
387
  'core.run.recoveryBusy': 'Recovery/rollback is already in progress (wait for the current recovery to finish)',
package/src/index.ts CHANGED
@@ -180,7 +180,7 @@ export const name = 'config-manager'
180
180
  export const inject = ['settings', 'credentials']
181
181
 
182
182
  /** Plugin version, kept in sync with package.json ("version"). */
183
- export const PLUGIN_VERSION = '0.2.2'
183
+ export const PLUGIN_VERSION = '0.2.3'
184
184
 
185
185
  /** Plugin own package name — excluded from its own exported plugins list. */
186
186
  const PLUGIN_NAME = 'dsh-config-manager'
@@ -41,13 +41,13 @@ export const zh = {
41
41
  'route.syncPreviewFailed': '同步预览失败',
42
42
  'route.syncSessionExpired': '同步会话不存在或已过期,请重新拉取预览',
43
43
  'route.syncConflictResolutionRequired': '冲突项 {id} 必须提供 resolution(useRemote/keepLocal/skip)',
44
- 'route.syncApplySummary': '一键同步应用:{count} 项{status}{ineffective}',
44
+ 'route.syncApplySummary': '从远端合并:{count} 项{status}{ineffective}',
45
45
  'route.syncRollbackIncomplete': '(回滚未完成)',
46
46
  'route.syncRolledBack': '(已回滚)',
47
47
  'route.syncIneffectiveSuffix': '({count} 项未生效)',
48
48
  'route.syncApplyPartial': '同步应用部分成功:有项未生效(见告警明细)',
49
49
  'route.syncApplyFailed': '同步应用未完全成功',
50
- 'route.syncRollbackSummary': '一键同步回滚({id})',
50
+ 'route.syncRollbackSummary': '从远端合并回滚({id})',
51
51
  // ---------- 事故恢复 ----------
52
52
  'route.recoverySessionRepairRollback': '会话修复回滚',
53
53
  'route.recoverySessionRepairDropped': '会话修复(丢弃重放重复行 {count} 行)',
@@ -82,13 +82,13 @@ export const en: Record<keyof typeof zh, string> = {
82
82
  'route.syncPreviewFailed': 'Sync preview failed',
83
83
  'route.syncSessionExpired': 'The sync session does not exist or has expired; pull a fresh preview',
84
84
  'route.syncConflictResolutionRequired': 'Conflict item {id} must provide a resolution (useRemote/keepLocal/skip)',
85
- 'route.syncApplySummary': 'One-click sync apply: {count} item(s){status}{ineffective}',
85
+ 'route.syncApplySummary': 'Merge from remote: {count} item(s){status}{ineffective}',
86
86
  'route.syncRollbackIncomplete': ' (rollback incomplete)',
87
87
  'route.syncRolledBack': ' (rolled back)',
88
88
  'route.syncIneffectiveSuffix': ' ({count} item(s) had no effect)',
89
89
  'route.syncApplyPartial': 'Sync apply partially succeeded: some items had no effect (see the warning details)',
90
90
  'route.syncApplyFailed': 'Sync apply did not fully succeed',
91
- 'route.syncRollbackSummary': 'One-click sync rollback ({id})',
91
+ 'route.syncRollbackSummary': 'Merge from remote rollback ({id})',
92
92
  'route.recoverySessionRepairRollback': 'Session repair rollback',
93
93
  'route.recoverySessionRepairDropped': 'Session repair (dropped {count} replayed duplicate row(s))',
94
94
  'route.recoverySessionRepairRejected': 'Session repair was rejected: {reason}',