dsh-model-groups 0.3.4 → 0.3.6
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 +148 -66
- package/lib/client.js +294 -4
- package/native/pi-ai.patch.json +507 -4
- package/native/settings-models.patch.json +408 -1
- package/package.json +6 -3
- package/scripts/build.mjs +6 -1
- package/scripts/doctor.mjs +22 -23
- package/scripts/native-patches.mjs +78 -13
- package/src/auto-patch.js +8 -3
- package/src/client.js +8 -4
- package/src/compatibility.js +22 -0
- package/src/index.js +26 -22
- package/src/maintenance-fallback.js +160 -0
- package/src/maintenance-host.js +250 -0
- package/src/maintenance-state.js +186 -0
- package/src/maintenance.js +188 -0
- package/src/settings-page.css +21 -0
- package/src/settings-page.js +64 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.mg-maintenance{box-sizing:border-box;display:flex;flex-direction:column;gap:12px;max-width:720px;min-width:0;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:13px;line-height:1.5}
|
|
2
|
+
.mg-maintenance h2{margin:0;font-size:18px;font-weight:600;line-height:26px}
|
|
3
|
+
.mg-maintenance h3{margin:0;font-size:14px;font-weight:600;line-height:22px}
|
|
4
|
+
.mg-maintenance p{margin:0;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px}
|
|
5
|
+
.mg-maintenance-card{display:flex;flex-direction:column;gap:12px;min-width:0;border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-xl,12px);background:var(--dsw-alias-settings-card-fill);padding:16px}
|
|
6
|
+
.mg-maintenance-row{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}
|
|
7
|
+
.mg-maintenance ul{padding-left:20px;margin:0;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}
|
|
8
|
+
.mg-maintenance-actions{display:flex;flex-wrap:wrap;gap:10px;align-items:center}
|
|
9
|
+
.mg-maintenance button{align-self:flex-start;font:inherit;font-weight:500;line-height:20px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-fill-tsp-secondary);border:.5px solid var(--dsw-alias-settings-card-stroke);border-radius:var(--dsw-radius-sm,6px);padding:8px 14px;min-height:40px;cursor:pointer;white-space:normal}
|
|
10
|
+
.mg-maintenance .mg-maintenance-primary:not(:disabled){border-color:var(--dsw-alias-state-business-primary);font-weight:600}
|
|
11
|
+
.mg-maintenance .mg-maintenance-secondary{font-size:12px}
|
|
12
|
+
@media(hover:hover){.mg-maintenance button:not(:disabled):hover{background:var(--dsw-alias-interactive-bg-hover)}}
|
|
13
|
+
.mg-maintenance button:disabled{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-fill-tsp-secondary);cursor:not-allowed;opacity:1}
|
|
14
|
+
.mg-maintenance .mg-maintenance-danger:not(:disabled),.mg-maintenance .mg-maintenance-error{color:var(--dsw-alias-state-error-primary)}
|
|
15
|
+
.mg-maintenance .mg-maintenance-help{color:var(--dsw-alias-label-secondary)}
|
|
16
|
+
.mg-maintenance-dialog{display:flex;flex-direction:column;gap:12px;border:1px solid var(--dsw-alias-state-business-primary);border-radius:var(--dsw-radius-sm,6px);padding:16px}
|
|
17
|
+
.mg-maintenance-warning{padding:10px;border-left:3px solid var(--dsw-alias-state-business-primary)}
|
|
18
|
+
.mg-maintenance summary{cursor:pointer;font-weight:500;min-height:28px}
|
|
19
|
+
.mg-maintenance details[open]>*+*{margin-top:12px}
|
|
20
|
+
.mg-maintenance :focus-visible{outline:2px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:3px}
|
|
21
|
+
@media(max-width:480px){.mg-maintenance-card,.mg-maintenance-dialog{padding:12px}.mg-maintenance button{max-width:100%}}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { compatibilityGuidance } from './compatibility.js';
|
|
2
|
+
export function ModelGroupsSettingsPage() {
|
|
3
|
+
const h = react.createElement;
|
|
4
|
+
const controller = react.useMemo(() => createMaintenanceController(), []);
|
|
5
|
+
const state = react.useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
|
|
6
|
+
const [pending, setPending] = react.useState(null);
|
|
7
|
+
const [copyStatus, setCopyStatus] = react.useState('');
|
|
8
|
+
const confirmationId = react.useId();
|
|
9
|
+
react.useEffect(() => { controller.activate(); controller.refresh(); return () => controller.dispose(); }, [controller]);
|
|
10
|
+
const snapshot = state.snapshot;
|
|
11
|
+
const phaseLabels = { idle: '检查完成,可选择操作', applied: '多密钥已应用(磁盘状态)', restored: '多密钥已关闭,插件仍保留(磁盘状态)', removing: '正在卸载,尚未确认完成', removed: '已确认关闭多密钥并卸载插件', failed: '维护失败,未确认完成' };
|
|
12
|
+
const updatesAvailable = snapshot?.patches.some(patch => patch.updateAvailable) ?? false;
|
|
13
|
+
const statusLabel = snapshot?.versionMismatch ? '网页与维护后端版本不一致,请先重启并刷新' : snapshot?.phase === 'idle'
|
|
14
|
+
? snapshot.patches.some(patch => patch.state === 'conflict') ? '磁盘文件校验冲突,已停止整批维护'
|
|
15
|
+
: snapshot.patches.some(patch => patch.applySupported === false) ? '旧补丁已识别,但此构建不支持新版协议补丁'
|
|
16
|
+
: updatesAvailable ? '旧多密钥补丁已应用,新版更新待确认'
|
|
17
|
+
: snapshot.patches.length && snapshot.patches.every(patch => patch.state === 'patched') ? '多密钥已启用(磁盘状态)'
|
|
18
|
+
: snapshot.patches.length && snapshot.patches.every(patch => patch.state === 'pristine') ? '多密钥已关闭(磁盘状态)' : '磁盘状态待处理,请查看操作限制'
|
|
19
|
+
: phaseLabels[snapshot?.phase];
|
|
20
|
+
const patchLabels = { patched: '已应用', pristine: '原版', conflict: '冲突(不会强制覆盖)' };
|
|
21
|
+
const actions = [['apply', updatesAvailable ? '更新多密钥补丁' : '启用多密钥'], ['restore-only', '关闭多密钥'], ['restore-and-uninstall', '关闭并卸载插件']];
|
|
22
|
+
const anyAllowed = actions.some(([action]) => maintenanceCanAction(snapshot, action));
|
|
23
|
+
const refresh = () => { setPending(null); controller.refresh(); };
|
|
24
|
+
const confirm = () => { const action = pending; setPending(null); if (action) controller.operate(action, true); };
|
|
25
|
+
const copy = async () => {
|
|
26
|
+
try { await globalThis.navigator.clipboard.writeText(maintenanceDiagnostic(state)); setCopyStatus('已复制脱敏诊断'); }
|
|
27
|
+
catch { setCopyStatus('无法复制,请检查浏览器剪贴板权限。'); }
|
|
28
|
+
};
|
|
29
|
+
return h('section', { className: 'mg-maintenance', 'aria-label': '模型分组设置', 'aria-busy': state.busy },
|
|
30
|
+
h('h2', null, '模型分组 · 多密钥管理'),
|
|
31
|
+
h('p', null, '选择操作,再确认一次即可。不会调用 AI,不会显示或删除 API 密钥。'),
|
|
32
|
+
snapshot?.pluginVersion && h('p', { className: 'mg-maintenance-help' }, `插件后端 ${snapshot.pluginVersion} · 宿主 ${snapshot.compatibility?.hostVersion ?? '版本未确认'}`),
|
|
33
|
+
h('section', { className: 'mg-maintenance-card', 'aria-label': '当前状态' },
|
|
34
|
+
h('div', { className: 'mg-maintenance-row' }, h('h3', null, '当前状态'), h('button', { type: 'button', disabled: state.busy, onClick: refresh }, state.busy ? '处理中…' : '刷新检查')),
|
|
35
|
+
h('p', { role: 'status', 'aria-live': 'polite', className: 'mg-maintenance-help' }, state.busy ? '正在检查或执行维护,请勿关闭或重复操作。' : snapshot ? statusLabel : '尚无可验证状态,请刷新检查。'),
|
|
36
|
+
state.error && h('p', { role: 'alert', className: 'mg-maintenance-error' }, state.error),
|
|
37
|
+
state.patchedWarning && h('p', { role: 'alert', className: 'mg-maintenance-error' }, '警告:从普通插件列表卸载只移除插件,仍会留下宿主补丁,可能造成升级、文件冲突及旧多密钥模型依赖问题。请使用这里的“关闭并卸载插件”。'),
|
|
38
|
+
snapshot?.restartRequired && h('p', { role: 'status' }, '本进程已实际修改宿主文件,需要重启原引擎并刷新;页面刷新或插件重载不等于进程重启。'),
|
|
39
|
+
compatibilityGuidance(snapshot?.compatibility) && h('p', { role: 'status', className: 'mg-maintenance-help' }, compatibilityGuidance(snapshot.compatibility)),
|
|
40
|
+
updatesAvailable && h('p', { role: 'status' }, '多密钥补丁有更新可用;确认后将备份、校验并更新已识别的旧补丁。'),
|
|
41
|
+
snapshot?.phase === 'removed' && !snapshot.versionMismatch && h('p', { role: 'status' }, '卸载已确认:仅清理本插件的两个浏览器显示偏好,密钥、历史和备份均保留。'),
|
|
42
|
+
snapshot?.warnings.length > 0 && h('p', { className: 'mg-maintenance-warning', role: 'status' }, '部分使用情况未验证,关闭前请阅读提示;检查提醒不会额外禁用操作。'),
|
|
43
|
+
snapshot?.blockers.length > 0 && h('div', { role: 'alert' }, h('strong', null, '操作限制(各按钮以安全权限为准)'), h('ul', null, ...snapshot.blockers.map((blocker, index) => h('li', { key: index }, maintenanceBlockerText(blocker))))),
|
|
44
|
+
state.cleanupWarning && h('p', { role: 'status' }, '卸载已确认,但浏览器阻止清理本插件的显示偏好;其他数据未受影响。')),
|
|
45
|
+
h('section', { className: 'mg-maintenance-card', 'aria-label': '多密钥操作' },
|
|
46
|
+
h('h3', null, '多密钥操作'),
|
|
47
|
+
h('div', { className: 'mg-maintenance-actions' }, ...actions.map(([action, label]) => h('button', { key: action, type: 'button', className: action === 'restore-and-uninstall' ? 'mg-maintenance-secondary mg-maintenance-danger' : 'mg-maintenance-primary', disabled: state.busy || !!pending || !maintenanceCanAction(snapshot, action), onClick: () => setPending(action) }, label))),
|
|
48
|
+
!anyAllowed && h('p', null, '请刷新检查或处理上方操作限制。检查提醒本身不会禁用按钮。'),
|
|
49
|
+
pending && h('div', { role: 'dialog', 'aria-labelledby': confirmationId, 'aria-describedby': `${confirmationId}-help`, className: 'mg-maintenance-dialog' },
|
|
50
|
+
h('h3', { id: confirmationId }, `确认${actions.find(([action]) => action === pending)[1]}?`),
|
|
51
|
+
h('p', { id: `${confirmationId}-help` }, pending === 'apply' ? '将备份、校验并修改宿主文件以启用多密钥。不会自动换服务或模型。请保存草稿、停止任务,完成后重启原引擎。' : '将校验并恢复宿主原版,关闭多密钥。密钥、配置和历史保留,不自动换服务或模型;旧多密钥选择和配置可能影响后续使用或重启。请保存草稿、停止任务,完成后重启原引擎。'),
|
|
52
|
+
h('p', null, '请先停止所有任务。部分使用情况未能检查不等于安全;检查提醒不会替代您的判断。'),
|
|
53
|
+
pending === 'restore-and-uninstall' && h('p', null, '确认原版恢复成功后才卸载插件;只有确认卸载完成才清理本插件的两个浏览器显示偏好。连接中断不代表成功。'),
|
|
54
|
+
h('div', { className: 'mg-maintenance-actions' }, h('button', { type: 'button', onClick: () => setPending(null) }, '取消'), h('button', { type: 'button', disabled: state.busy || !maintenanceCanAction(snapshot, pending), onClick: confirm }, '确认执行')))),
|
|
55
|
+
h('details', { className: 'mg-maintenance-card' },
|
|
56
|
+
h('summary', null, '检查详情与脱敏诊断'),
|
|
57
|
+
h('p', null, '这里只验证磁盘和安全权限,运行时是否已切换未知。每次确认后会重新检查;状态变化需重新确认。未知文件不会强制覆盖,检查不完整仅提醒;操作仍以各自安全权限为准。'),
|
|
58
|
+
snapshot?.warnings.length > 0 && h('ul', null, ...snapshot.warnings.map((warning, index) => h('li', { key: index }, maintenanceWarningText(warning)))),
|
|
59
|
+
snapshot && h('ul', null, ...snapshot.patches.map((patch, index) => h('li', { key: index }, `宿主文件 ${index + 1}:${patchLabels[patch.state]}${patch.applySupported === false ? '(此构建不支持新版补丁)' : patch.updateAvailable ? '(更新可用)' : ''}`))),
|
|
60
|
+
snapshot?.compatibility && h('ul', null, ...snapshot.compatibility.files.map(item => h('li', { key: item.id }, `${item.id} · ${item.installedVersion ?? '版本未知'} · ${item.code} · SHA-256 ${item.observedSha256 ?? '不可读取'}`))),
|
|
61
|
+
h('p', null, '诊断仅含程序版本、固定文件标识与程序 SHA-256,不含文件路径、配置正文、密钥或聊天。升级失败请先保留现场,不要用另一构建的文件覆盖。'),
|
|
62
|
+
h('button', { type: 'button', onClick: copy }, '复制脱敏诊断'),
|
|
63
|
+
h('p', { role: 'status' }, copyStatus)));
|
|
64
|
+
}
|