@qic-9821/dsh-plugin-sync 0.1.1 → 0.1.2
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 +5 -1
- package/client.js +46 -8
- package/lib/http.js +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ dsh plugin --profile <profile> add @qic-9821/dsh-plugin-sync
|
|
|
40
40
|
| 允许对端改动本机 | `capRemoteApply` | 关掉后对端连读都读不到 |
|
|
41
41
|
| 同步密钥字段 | `capSecrets` | 默认关;`appSecret` / `token` / `*key*` 一律写成 `[redacted]` |
|
|
42
42
|
|
|
43
|
+
默认只开「插件清单与启停」和「允许对端改动本机」;配置、安装、复制二进制、密钥四项要自己在设置页打开,打开后下一次自动同步就会真的落地对应操作。
|
|
44
|
+
|
|
43
45
|
安装、升级、卸载、启停全部走宿主的 `pluginManager` 服务(`installBundle` / `updateBundle` / `removeBundle` / `setBundleEnabled` / `setPluginEnabled`),不自己拼 pnpm 命令行、不手写 profile 的 `package.json`,失败由宿主回滚它自己的改动。
|
|
44
46
|
|
|
45
47
|
## 怎么连
|
|
@@ -55,9 +57,11 @@ dsh plugin --profile <profile> add @qic-9821/dsh-plugin-sync
|
|
|
55
57
|
|
|
56
58
|
- `autoStart`:DSH 启动后约 25 秒跑一轮(等宿主把插件挂完)。
|
|
57
59
|
- `autoRestart`:进程重启后再补一轮(约 2 分钟),处理对端还在启动的情况;服务器重启后的同步就是这条。
|
|
58
|
-
- `intervalMinutes`:固定间隔(0 =
|
|
60
|
+
- `intervalMinutes`:固定间隔(0 = 关)。只在 DSH 运行期间生效,每 N 分钟跑一轮完整同步;和「启动时」「重启后」两轮互不影响。
|
|
59
61
|
- 手动:面板/设置页按钮,或工具 `plugin_sync` `action="run"`。
|
|
60
62
|
|
|
63
|
+
总开关 `enabled` 关掉后,以上任何自动同步与手动同步都不执行(仍可在面板上「只看计划」)。
|
|
64
|
+
|
|
61
65
|
## 冲突策略
|
|
62
66
|
|
|
63
67
|
- `local`:以发起方为准(默认)
|
package/client.js
CHANGED
|
@@ -94,6 +94,9 @@ window.__ModuleLoader__.load({
|
|
|
94
94
|
}),
|
|
95
95
|
h('span', { key: 'text' }, [
|
|
96
96
|
h('b', { key: 'name' }, props.label),
|
|
97
|
+
props.dirty
|
|
98
|
+
? h('span', { key: 'dirty', style: { marginLeft: 6, fontSize: 11, color: 'var(--dsh-accent, #3b82f6)' } }, '未保存')
|
|
99
|
+
: null,
|
|
97
100
|
props.desc ? h('div', { key: 'desc', style: MUTED }, props.desc) : null,
|
|
98
101
|
]),
|
|
99
102
|
],
|
|
@@ -125,8 +128,18 @@ window.__ModuleLoader__.load({
|
|
|
125
128
|
}, [load])
|
|
126
129
|
|
|
127
130
|
const config = state?.config || {}
|
|
128
|
-
const
|
|
131
|
+
const caps = config.capabilities || {}
|
|
132
|
+
// draft wins, then the nested capabilities map, then the flat key
|
|
133
|
+
const value = useCallback(
|
|
134
|
+
(key) => (draft[key] !== undefined ? draft[key] : caps[key] !== undefined ? caps[key] : config[key]),
|
|
135
|
+
[draft, caps, config],
|
|
136
|
+
)
|
|
137
|
+
const dirty = useCallback((key) => draft[key] !== undefined, [draft])
|
|
129
138
|
const setValue = useCallback((key, next) => setDraft((current) => ({ ...current, [key]: next })), [])
|
|
139
|
+
const capsOn = useMemo(
|
|
140
|
+
() => (state?.capabilityCatalog || []).filter((cap) => Boolean(value(cap.key))).length,
|
|
141
|
+
[state, value],
|
|
142
|
+
)
|
|
130
143
|
|
|
131
144
|
const save = useCallback(async () => {
|
|
132
145
|
if (Object.keys(draft).length === 0) return
|
|
@@ -190,9 +203,10 @@ window.__ModuleLoader__.load({
|
|
|
190
203
|
? `${formatTime(status.lastRun.at)} · ${status.lastRun.ok ? '成功' : `失败:${status.lastRun.error || ''}`}`
|
|
191
204
|
: '还没跑过',
|
|
192
205
|
],
|
|
206
|
+
['设置保存于', state?.updatedAt ? formatTime(state.updatedAt) : '还没保存过'],
|
|
193
207
|
['当前', status.running ? '同步中…' : '空闲'],
|
|
194
208
|
]
|
|
195
|
-
}, [status])
|
|
209
|
+
}, [status, state?.updatedAt])
|
|
196
210
|
|
|
197
211
|
if (!state) {
|
|
198
212
|
return h('div', { style: { padding: 16, fontSize: 13 } }, error ? `读取失败:${error}` : '读取中…')
|
|
@@ -260,7 +274,8 @@ window.__ModuleLoader__.load({
|
|
|
260
274
|
h(Toggle, {
|
|
261
275
|
key: 'enable',
|
|
262
276
|
label: '总开关',
|
|
263
|
-
desc: '
|
|
277
|
+
desc: '关掉后任何自动与手动同步都不执行(仍可「只看计划」)',
|
|
278
|
+
dirty: dirty('enabled'),
|
|
264
279
|
checked: Boolean(value('enabled')),
|
|
265
280
|
onChange: (next) => setValue('enabled', next),
|
|
266
281
|
}),
|
|
@@ -268,6 +283,7 @@ window.__ModuleLoader__.load({
|
|
|
268
283
|
key: 'startup',
|
|
269
284
|
label: '软件启动时同步',
|
|
270
285
|
desc: 'DSH 启动后约 25 秒跑一轮(等宿主把插件都挂上)',
|
|
286
|
+
dirty: dirty('autoStart'),
|
|
271
287
|
checked: Boolean(value('autoStart')),
|
|
272
288
|
onChange: (next) => setValue('autoStart', next),
|
|
273
289
|
}),
|
|
@@ -275,22 +291,38 @@ window.__ModuleLoader__.load({
|
|
|
275
291
|
key: 'restart',
|
|
276
292
|
label: '服务器重启后同步',
|
|
277
293
|
desc: '进程重启之后再补一轮,处理对端还在启动的情况',
|
|
294
|
+
dirty: dirty('autoRestart'),
|
|
278
295
|
checked: Boolean(value('autoRestart')),
|
|
279
296
|
onChange: (next) => setValue('autoRestart', next),
|
|
280
297
|
}),
|
|
281
|
-
h(
|
|
282
|
-
|
|
283
|
-
|
|
298
|
+
h(Toggle, {
|
|
299
|
+
key: 'interval',
|
|
300
|
+
label: '定时同步',
|
|
301
|
+
desc: 'DSH 运行期间每 N 分钟跑一轮;关掉就是「不按间隔」,和上面两轮互不影响',
|
|
302
|
+
dirty: dirty('intervalMinutes'),
|
|
303
|
+
checked: Number(value('intervalMinutes')) > 0,
|
|
304
|
+
onChange: (next) =>
|
|
305
|
+
setValue('intervalMinutes', next ? Number(value('intervalMinutes')) || 15 : 0),
|
|
306
|
+
}),
|
|
307
|
+
Number(value('intervalMinutes')) > 0
|
|
308
|
+
? h(
|
|
309
|
+
Field,
|
|
310
|
+
{ key: 'intervalValue', label: '间隔分钟数(1–1440)' },
|
|
311
|
+
input(value('intervalMinutes'), '15', (next) => setValue('intervalMinutes', Math.max(0, Number(next) || 0))),
|
|
312
|
+
)
|
|
313
|
+
: null,
|
|
314
|
+
h(Field, { key: 'delay', label: '启动后延迟秒数(只影响「软件启动时同步」)' },
|
|
284
315
|
input(value('startupDelaySec'), '25', (next) => setValue('startupDelaySec', Number(next) || 0))),
|
|
285
316
|
]),
|
|
286
317
|
]),
|
|
287
318
|
|
|
288
|
-
h(Card, { key: 'caps', title:
|
|
319
|
+
h(Card, { key: 'caps', title: `权限(已开启 ${capsOn} / ${(state.capabilityCatalog || []).length},每一项可以单独关)` },
|
|
289
320
|
(state.capabilityCatalog || []).map((cap) =>
|
|
290
321
|
h(Toggle, {
|
|
291
322
|
key: cap.key,
|
|
292
323
|
label: cap.label,
|
|
293
324
|
desc: cap.desc,
|
|
325
|
+
dirty: dirty(cap.key),
|
|
294
326
|
checked: Boolean(value(cap.key)),
|
|
295
327
|
onChange: (next) => setValue(cap.key, next),
|
|
296
328
|
}),
|
|
@@ -299,7 +331,13 @@ window.__ModuleLoader__.load({
|
|
|
299
331
|
|
|
300
332
|
h('div', { key: 'save', style: { display: 'flex', gap: 8, alignItems: 'center' } }, [
|
|
301
333
|
h('button', { key: 'save', style: BUTTON_PRIMARY, disabled: Boolean(busy) || Object.keys(draft).length === 0, onClick: () => void save() }, '保存设置'),
|
|
302
|
-
h(
|
|
334
|
+
h(
|
|
335
|
+
'span',
|
|
336
|
+
{ key: 'hint', style: MUTED },
|
|
337
|
+
Object.keys(draft).length === 0
|
|
338
|
+
? `已保存 · ${state?.updatedAt ? formatTime(state.updatedAt) : '(本次打开后还没改过)'}`
|
|
339
|
+
: `${Object.keys(draft).length} 项待保存`,
|
|
340
|
+
),
|
|
303
341
|
]),
|
|
304
342
|
|
|
305
343
|
out ? h('pre', { key: 'out', style: { marginTop: 12, whiteSpace: 'pre-wrap', fontSize: 11.5, maxHeight: 260, overflow: 'auto', background: 'rgba(127,127,127,.1)', padding: 10, borderRadius: 8 } }, out) : null,
|
package/lib/http.js
CHANGED
|
@@ -148,6 +148,8 @@ export async function statePayload(engine, cfg) {
|
|
|
148
148
|
capabilities: Object.fromEntries(
|
|
149
149
|
Object.keys(CAPABILITY_LABELS).map((key) => [key, Boolean(cfg[key])]),
|
|
150
150
|
),
|
|
151
|
+
// flat mirror of the capabilities, so consumers can read `capPlugins` directly
|
|
152
|
+
...Object.fromEntries(Object.keys(CAPABILITY_LABELS).map((key) => [key, Boolean(cfg[key])])),
|
|
151
153
|
include: cfg.include,
|
|
152
154
|
exclude: cfg.exclude,
|
|
153
155
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qic-9821/dsh-plugin-sync",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Keep the DSH plugin set (inventory, configs, enable state, package installs, local bundle binaries) in sync between two machines, with per-capability switches, schedule controls and a settings/panel UI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "QIC-9821",
|