dsh-session-guard 0.1.2 → 0.1.5-beta.1
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/CHANGELOG.ja.md +52 -29
- package/CHANGELOG.ko.md +52 -29
- package/CHANGELOG.md +94 -29
- package/INSTALL.ja.md +83 -42
- package/INSTALL.ko.md +83 -42
- package/INSTALL.md +86 -44
- package/INSTALL.zh.md +82 -42
- package/README.en.md +241 -170
- package/README.ja.md +149 -106
- package/README.ko.md +149 -106
- package/README.md +86 -9
- package/dsh.plugin.json +13 -0
- package/lib/client.js +462 -270
- package/lib/client.js.map +1 -1
- package/package.json +101 -78
- package/src/client/badge-text.ts +44 -0
- package/src/client/locales.ts +163 -95
- package/src/client/settings-card.tsx +370 -191
- package/src/client/status-badge.tsx +55 -66
- package/src/deferrals.js +197 -0
- package/src/index.js +303 -293
- package/src/pause-gate.js +451 -416
- package/src/provider-directory.js +101 -0
- package/src/provider.js +139 -0
- package/src/request-guard.js +191 -0
- package/src/retry.js +227 -202
- package/src/settings.js +114 -97
- package/src/targets.js +70 -0
- package/src/time.js +195 -104
- package/src/tool-call-id.js +44 -0
- package/src/wiring.js +263 -0
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","useSyncExternalStore","useState"],"sources":["../src/client/status-badge.tsx","../src/client/settings-card.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * dsh-session-guard — 状态徽标(纯展示,fail-open)。\n *\n * 轮询 host 的 /session-guard/status(全局当前阶段),显示 高峰/谷时/周末。\n * 仅展示,不做任何队列/会话动作;冻结按钮由 input-traffic 经桥接管(D6/D8)。\n */\nimport { useEffect, useState } from 'react'\n\n/** slot 运行时注入的会话级 props(sessionId 由 dsh 的 SessionStandardProps 提供)。 */\nexport interface StatusBadgeProps {\n sessionId?: string\n}\n\n/** /session-guard/status 返回的全局阶段。 */\nexport interface Status {\n phase: 'peak' | 'off-peak' | 'weekend'\n enabled: boolean\n weekendMode: boolean\n timezone: string\n}\n\nconst LABELS = {\n peak: '高峰',\n 'off-peak': '谷时',\n weekend: '周末',\n} as const\n\nconst POLL_MS = 15_000\n\n/** 状态徽标:轮询全局阶段,显示 高峰/谷时/周末(enabled 关闭或请求失败时静默隐藏)。 */\nexport function StatusBadge({ sessionId }: StatusBadgeProps) {\n const [status, setStatus] = useState<Status | null>(null)\n\n useEffect(() => {\n if (!sessionId) return\n let cancelled = false\n const poll = async () => {\n try {\n const res = await fetch('/session-guard/status')\n const body = await res.json().catch(() => null)\n if (!cancelled && body?.ok && body.status) setStatus(body.status)\n } catch {\n // fail-open:路由/网络不可达 → 静默,徽标隐藏。\n }\n }\n void poll()\n const timer = setInterval(poll, POLL_MS)\n return () => {\n cancelled = true\n clearInterval(timer)\n }\n }, [sessionId])\n\n if (!status || !status.enabled) return null\n\n const cls = status.phase === 'peak' ? 'sg-peak' : status.phase === 'weekend' ? 'sg-weekend' : 'sg-off'\n return (\n <span\n className={`sg-status ${cls}`}\n title={`${LABELS[status.phase]} · ${status.timezone}${status.weekendMode ? ' · 周末模式' : ''}`}\n data-sg-phase={status.phase}\n >\n {LABELS[status.phase]}\n </span>\n )\n}\n","/**\n * dsh-session-guard — 插件配置卡片(settings.plugin.item 面)。\n *\n * 对齐 dsh-thinking-levels / dsh-tidychat 的卡片语法:一个可展开的 `<li>`,\n * header 按钮(插件名 + 描述 + chevron)切换字段体;开关为 pill switch\n * (track + thumb),不是复选框对勾。\n *\n * 通过 `settingsScope.bind({ namespace: NS })` 绑定 host 已注册的\n * `session-guard` 命名空间;每次变更立即经 scope 提交(无 staged form)。\n * 仅依赖 react;CSS 经 `<style data-plugin-css>` 注入一次,控件为原生 HTML,\n * 客户端 bundle 无 value-import @deepseek-ai/* 平台包(类型导入被构建擦除)。\n */\nimport { useState, useSyncExternalStore } from 'react'\nimport type { JSX } from 'react'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\n\n/** 命名空间值形状(与 host 的 DEFAULT_SETTINGS 对应)。 */\nexport interface SessionGuardConfig {\n enabled: boolean\n offPeakAutoResume: boolean\n weekendMode: boolean\n queueFallback: boolean\n retryEnabled: boolean\n timezone: string\n pauseMode: 'safe' | 'force'\n pauseReason: 'wait' | 'stop'\n}\n\n/** 注入给卡片的面:绑定到 session-guard 命名空间的 settings scope。 */\nexport interface SettingsCardInjected {\n scope: SettingsScope<SessionGuardConfig>\n}\n\nexport type SettingsCardProps = SettingsCardInjected\n\n/** 卡片样式,注入一次(保持 bundle CSS-free)。 */\nconst CARD_CSS = `\n.sgCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}\n.sgCard:hover{border-color:var(--dsw-alias-label-dimmed)}\n.sgCard-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}\n.sgCardHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}\n.sgCardHeadtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}\n.sgCardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}\n.sgCardDesc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}\n.sgCardChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}\n.sgCardChevron-open{transform:rotate(180deg)}\n.sgCardBody{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}\n.sgRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}\n.sgRow:last-child{border-bottom:0}\n.sgRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}\n.sgTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}\n.sgDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}\n.sgSwitch{position:relative;width:40px;height:22px;flex:none}\n.sgSwitch>input{position:absolute;inset:0;width:100%;height:100%;opacity:0;margin:0;cursor:pointer}\n.sgSwitch>input:disabled{cursor:not-allowed}\n.sgSwitchTrack{position:absolute;inset:0;border-radius:22px;background:var(--dsw-alias-interactive-bg-hover);transition:background .16s}\n.sgSwitch>input:checked+.sgSwitchTrack{background:var(--dsw-alias-button-primary-fill)}\n.sgSwitchThumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .16s}\n.sgSwitch>input:checked~.sgSwitchThumb{transform:translateX(18px)}\n.sgReadonly{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:8px 0 0}\n`\n\n/** 注入一次卡片样式。 */\nfunction injectCss(): void {\n if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css=\"session-guard-card\"]') === null) {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-session-guard'\n tag.dataset.pluginCss = 'session-guard-card'\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n }\n}\n\n/** 一行 pill switch(滑块开关,绑定 scope)。 */\nfunction SwitchRow(props: {\n label: string\n description?: string\n checked: boolean\n disabled: boolean\n onChange: (_next: boolean) => void\n}): JSX.Element {\n const { label, description, checked, disabled, onChange } = props\n return (\n <div className=\"sgRow\">\n <div className=\"sgRowText\">\n <div className=\"sgTitle\">{label}</div>\n {description !== undefined && <div className=\"sgDesc\">{description}</div>}\n </div>\n <label className=\"sgSwitch\">\n <input\n type=\"checkbox\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => onChange(e.currentTarget.checked)}\n />\n <span className=\"sgSwitchTrack\" />\n <span className=\"sgSwitchThumb\" />\n </label>\n </div>\n )\n}\n\n/** 插件配置卡片主体:可展开的 <li> + header 按钮 + pill switch 字段体。 */\nexport function SessionGuardCard({ scope }: SettingsCardProps): JSX.Element {\n const snapshot = useSyncExternalStore(\n (listener) => scope.subscribe(listener),\n () => scope.getSnapshot(),\n )\n const unavailable = snapshot.status === 'unavailable'\n const readonly = unavailable || !snapshot.writable\n const value = (snapshot.value ?? {}) as Partial<SessionGuardConfig>\n const [open, setOpen] = useState(false)\n\n injectCss()\n\n const toggle = (field: keyof SessionGuardConfig, next: boolean) => { void scope.set(field, next) }\n\n return (\n <li className={'sgCard' + (open ? ' sgCard-open' : '')}>\n <button\n type=\"button\"\n className=\"sgCardHeader\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n <span className=\"sgCardHeadtext\">\n <span className=\"sgCardName\">会话守护门禁</span>\n <span className=\"sgCardDesc\">高峰自动暂停运行会话,周末模式无视峰谷畅快跑</span>\n </span>\n <svg\n className={'sgCardChevron' + (open ? ' sgCardChevron-open' : '')}\n viewBox=\"0 0 14 14\"\n width={14}\n height={14}\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path d=\"M3.5 5.5L7 9l3.5-3.5\" stroke=\"currentColor\" strokeWidth={1.5} strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </button>\n {open && (\n <div className=\"sgCardBody\">\n {unavailable ? (\n <p style={{ margin: '0', padding: '12px 0', fontSize: '13px', color: 'var(--dsw-alias-label-tertiary)' }}>\n 设置命名空间不可用:请确认 dsh-session-guard 已装配进此 profile。\n </p>\n ) : (\n <>\n <SwitchRow\n label=\"高峰自动暂停冻结会话\"\n description=\"高峰时段自动暂停运行会话\"\n checked={value.enabled ?? true}\n disabled={readonly}\n onChange={(next) => toggle('enabled', next)}\n />\n <SwitchRow\n label=\"低谷自动恢复\"\n description=\"低峰时段自动恢复被暂停的会话\"\n checked={value.offPeakAutoResume ?? true}\n disabled={readonly}\n onChange={(next) => toggle('offPeakAutoResume', next)}\n />\n <SwitchRow\n label=\"周末模式\"\n description=\"识别周末,无视峰谷畅快跑\"\n checked={value.weekendMode ?? true}\n disabled={readonly}\n onChange={(next) => toggle('weekendMode', next)}\n />\n <SwitchRow\n label=\"回退锁队列\"\n description=\"无会话门时锁等待队列\"\n checked={value.queueFallback ?? true}\n disabled={readonly}\n onChange={(next) => toggle('queueFallback', next)}\n />\n <SwitchRow\n label=\"自动重试\"\n description=\"后端重试,默认关(保守)\"\n checked={value.retryEnabled ?? false}\n disabled={readonly}\n onChange={(next) => toggle('retryEnabled', next)}\n />\n {!snapshot.writable && <p className=\"sgReadonly\">当前只读,无法修改。</p>}\n </>\n )}\n </div>\n )}\n </li>\n )\n}\n","/**\n * dsh-session-guard — 浏览器 half。\n *\n * 职责(全部 fail-open,D8):\n * - 在 composer 输入区右侧注册一个**纯展示**状态徽标(高峰/谷时/周末),轮询\n * /session-guard/status;\n * - 注册 `settings.plugin.item` 设置卡片,经 `settingsScope.bind({ namespace })`\n * 绑定 host 已注册的 `session-guard` 命名空间——这正是“插件配置”面板显示本\n * 插件的**必要**机制(对齐 dsh-thinking-levels / dsh-context);\n * - **不做**冻结/会话动作——冻结按钮由 input-traffic 接管并经 /session-guard/rpc\n * 桥接 host 会话门;本插件客户端不注册任何按钮,避免与 input-traffic 冲突。\n *\n * 构建:tsdown → lib/client.js(__ModuleLoader__.load 注册,与 input-traffic 同构)。\n */\nimport { StatusBadge } from './status-badge'\nimport { SessionGuardCard } from './settings-card'\n\n/** 客户端所需服务:slots(状态徽标 + 设置卡片)+ locale + settingsScope(设置卡片绑定)。 */\nexport const inject = ['slots', 'locale', 'settingsScope']\n\n/** 轻量 ctx 类型(仅本客户端用到的方法;构建时类型被剥离)。 */\ninterface SlotsFace {\n inject: (_name: string, _fn: () => unknown) => () => void\n register: (_options: Record<string, unknown>, _component: unknown) => () => void\n}\ninterface SettingsScopeFace {\n bind: (_spec: { namespace: string }) => unknown\n}\ninterface ClientCtx {\n slots: SlotsFace\n settingsScope: SettingsScopeFace\n}\n\n/** host 侧 src/settings.js 注册的命名空间(保持一致)。 */\nconst NS = 'session-guard'\n\nexport function apply(ctx: ClientCtx) {\n // 状态徽标:输入区右侧,纯展示。\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register({\n name: 'conversation.input.right',\n id: 'session-guard-status',\n order: 50,\n locale: 'session-guard',\n }, StatusBadge))\n\n // 插件配置卡片:绑定 session-guard 命名空间,经 settingsScope 渲染并提交。\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: NS,\n key: NS,\n locale: 'session-guard',\n inject: () => ({\n scope: ctx.settingsScope.bind({ namespace: NS }),\n }),\n }, SessionGuardCard))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","useState","useSyncExternalStore"],"sources":["../src/client/badge-text.ts","../src/client/status-badge.tsx","../src/client/settings-card.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * dsh-session-guard — 状态徽标文案(纯函数,零依赖,可单测)。\n *\n * 分离出来的原因:徽标语义(高峰期「只拦官方」vs「全部暂停」)需要单测,\n * 而组件文件是 TSX(含 React 运行时)。这里只有字符串逻辑。\n */\n\n/** /session-guard/status 返回的全局阶段(组件与测试共用)。 */\nexport interface Status {\n phase: 'peak' | 'off-peak' | 'weekend'\n enabled: boolean\n weekendMode: boolean\n /** 官方源二维判定是否开启(开启时高峰期只拦官方源)。 */\n providerGuard?: boolean\n /** 当前被挂起的请求数(hold 模式)。 */\n held?: number\n /** error 模式记下的延后会话数。 */\n deferred?: number\n timezone: string\n}\n\n/** 阶段文案。 */\nexport const PHASE_LABELS = {\n peak: '高峰',\n 'off-peak': '谷时',\n weekend: '周末',\n} as const\n\n/** 高峰期的徽标文案:二维判定开启时区分「只拦官方」与「全部暂停」。 */\nexport function peakLabel(status: Pick<Status, 'phase' | 'providerGuard'>): string {\n if (status.phase !== 'peak') return PHASE_LABELS[status.phase]\n return status.providerGuard === true ? '高峰·拦官方' : '高峰·全部暂停'\n}\n\n/** 悬浮说明:把判定口径与当前挂起/延后数量写清楚。 */\nexport function badgeTitle(status: Status): string {\n const base = `${PHASE_LABELS[status.phase]} · ${status.timezone}${status.weekendMode ? ' · 周末模式' : ''}`\n if (status.phase !== 'peak') return base\n const mode = status.providerGuard === true ? '仅拦截 DeepSeek 官方源' : '全部会话暂停(未启用二维判定)'\n const held = status.held ?? 0\n const deferred = status.deferred ?? 0\n const extra = held > 0 || deferred > 0 ? ` · 挂起 ${held} · 延后 ${deferred}` : ''\n return `${base} · ${mode}${extra}`\n}\n","/**\n * dsh-session-guard — 状态徽标(纯展示,fail-open)。\n *\n * 轮询 host 的 /session-guard/status(全局当前阶段),显示 高峰/谷时/周末;\n * 高峰期按二维判定区分「只拦官方」与「全部暂停」(文案见 badge-text.ts)。\n * 仅展示,不做任何队列/会话动作;冻结按钮由 input-traffic 经桥接管(D6/D8)。\n */\nimport { useEffect, useState } from 'react'\nimport { badgeTitle, peakLabel, type Status } from './badge-text'\n\n/** slot 运行时注入的会话级 props(sessionId 由 dsh 的 SessionStandardProps 提供)。 */\nexport interface StatusBadgeProps {\n sessionId?: string\n}\n\nconst POLL_MS = 15_000\n\n/** 状态徽标:轮询全局阶段,显示 高峰/谷时/周末(enabled 关闭或请求失败时静默隐藏)。 */\nexport function StatusBadge({ sessionId }: StatusBadgeProps) {\n const [status, setStatus] = useState<Status | null>(null)\n\n useEffect(() => {\n if (!sessionId) return\n let cancelled = false\n const poll = async () => {\n try {\n const res = await fetch('/session-guard/status')\n const body = await res.json().catch(() => null)\n if (!cancelled && body?.ok && body.status) setStatus(body.status)\n } catch {\n // fail-open:路由/网络不可达 → 静默,徽标隐藏。\n }\n }\n void poll()\n const timer = setInterval(poll, POLL_MS)\n return () => {\n cancelled = true\n clearInterval(timer)\n }\n }, [sessionId])\n\n if (!status || !status.enabled) return null\n\n const cls = status.phase === 'peak' ? 'sg-peak' : status.phase === 'weekend' ? 'sg-weekend' : 'sg-off'\n return (\n <span\n className={`sg-status ${cls}`}\n title={badgeTitle(status)}\n data-sg-phase={status.phase}\n data-sg-provider-guard={status.providerGuard === true ? 'on' : 'off'}\n >\n {peakLabel(status)}\n </span>\n )\n}\n","/**\n * dsh-session-guard — 插件配置卡片(settings.plugin.item 面)。\n *\n * 对齐 dsh-thinking-levels / dsh-tidychat 的卡片语法:一个可展开的 `<li>`,\n * header 按钮(插件名 + 描述 + chevron)切换字段体;开关为 pill switch\n * (track + thumb),不是复选框对勾。\n *\n * 通过 `settingsScope.bind({ namespace: NS })` 绑定 host 已注册的\n * `session-guard` 命名空间;每次变更立即经 scope 提交(无 staged form)。\n * 仅依赖 react;CSS 经 `<style data-plugin-css>` 注入一次,控件为原生 HTML,\n * 客户端 bundle 无 value-import @deepseek-ai/* 平台包(类型导入被构建擦除)。\n */\nimport { useEffect, useState, useSyncExternalStore } from 'react'\nimport type { JSX } from 'react'\nimport type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client'\n\n/** 命名空间值形状(与 host 的 DEFAULT_SETTINGS 对应)。 */\nexport interface SessionGuardConfig {\n enabled: boolean\n offPeakAutoResume: boolean\n weekendMode: boolean\n queueFallback: boolean\n retryEnabled: boolean\n timezone: string\n pauseMode: 'safe' | 'force'\n pauseReason: 'wait' | 'stop'\n // ── 官方 provider 二维判定(高峰 × 目标源)──\n providerGuard: boolean\n officialProviders: string[]\n officialBaseURLs: string[]\n deferredResume: boolean\n deferredResumeText: string\n deferredMode: 'hold' | 'error'\n deferredMaxHoldMs: number\n guardSubagents: boolean\n}\n\n/** 注入给卡片的面:绑定到 session-guard 命名空间的 settings scope。 */\nexport interface SettingsCardInjected {\n scope: SettingsScope<SessionGuardConfig>\n}\n\nexport type SettingsCardProps = SettingsCardInjected\n\n/** 卡片样式,注入一次(保持 bundle CSS-free)。 */\nconst CARD_CSS = `\n.sgCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}\n.sgCard:hover{border-color:var(--dsw-alias-label-dimmed)}\n.sgCard-open{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}\n.sgCardHeader{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}\n.sgCardHeadtext{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}\n.sgCardName{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}\n.sgCardDesc{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}\n.sgCardChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}\n.sgCardChevron-open{transform:rotate(180deg)}\n.sgCardBody{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding:4px 0 12px}\n.sgRow{border-bottom:1px solid var(--dsw-alias-border-l2);align-items:center;gap:8px;padding:16px 0;display:flex}\n.sgRow:last-child{border-bottom:0}\n.sgRowText{flex-direction:column;flex:1;gap:4px;min-width:0;padding-right:48px;display:flex}\n.sgRowText-wide{padding-right:0}\n.sgTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:400;line-height:22px}\n.sgDesc{color:var(--dsw-alias-label-tertiary);font-size:12px;font-weight:400;line-height:18px}\n.sgSwitch{position:relative;width:40px;height:22px;flex:none}\n.sgSwitch>input{position:absolute;inset:0;width:100%;height:100%;opacity:0;margin:0;cursor:pointer}\n.sgSwitch>input:disabled{cursor:not-allowed}\n.sgSwitchTrack{position:absolute;inset:0;border-radius:22px;background:var(--dsw-alias-interactive-bg-hover);transition:background .16s}\n.sgSwitch>input:checked+.sgSwitchTrack{background:var(--dsw-alias-button-primary-fill)}\n.sgSwitchThumb{position:absolute;top:2px;left:2px;width:18px;height:18px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .16s}\n.sgSwitch>input:checked~.sgSwitchThumb{transform:translateX(18px)}\n.sgInput{appearance:none;min-width:0;width:100%;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;margin-top:8px}\n.sgInput:disabled{opacity:.6;cursor:not-allowed}\n.sgSelect{appearance:none;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:7px 10px;min-width:160px;flex:none}\n.sgSelect:disabled{opacity:.6;cursor:not-allowed}\n.sgHint{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;margin:6px 0 0}\n.sgReadonly{color:var(--dsw-alias-label-tertiary);font-size:12px;margin:8px 0 0}\n`\n\n/** 注入一次卡片样式。 */\nfunction injectCss(): void {\n if (typeof document !== 'undefined' && document.querySelector('style[data-plugin-css=\"session-guard-card\"]') === null) {\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-session-guard'\n tag.dataset.pluginCss = 'session-guard-card'\n tag.textContent = CARD_CSS\n document.head.appendChild(tag)\n }\n}\n\n/** 一行 pill switch(滑块开关,绑定 scope)。 */\nfunction SwitchRow(props: {\n label: string\n description?: string\n checked: boolean\n disabled: boolean\n onChange: (_next: boolean) => void\n}): JSX.Element {\n const { label, description, checked, disabled, onChange } = props\n return (\n <div className=\"sgRow\">\n <div className=\"sgRowText\">\n <div className=\"sgTitle\">{label}</div>\n {description !== undefined && <div className=\"sgDesc\">{description}</div>}\n </div>\n <label className=\"sgSwitch\">\n <input\n type=\"checkbox\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => onChange(e.currentTarget.checked)}\n />\n <span className=\"sgSwitchTrack\" />\n <span className=\"sgSwitchThumb\" />\n </label>\n </div>\n )\n}\n\n/** 一行文本输入(失焦提交;外部值变化时同步草稿)。 */\nfunction TextRow(props: {\n label: string\n description?: string\n value: string\n placeholder?: string\n disabled: boolean\n onCommit: (_next: string) => void\n}): JSX.Element {\n const { label, description, value, placeholder, disabled, onCommit } = props\n const [draft, setDraft] = useState(value)\n useEffect(() => {\n setDraft(value)\n }, [value])\n return (\n <div className=\"sgRow\">\n <div className=\"sgRowText sgRowText-wide\">\n <div className=\"sgTitle\">{label}</div>\n {description !== undefined && <div className=\"sgDesc\">{description}</div>}\n <input\n className=\"sgInput\"\n type=\"text\"\n value={draft}\n placeholder={placeholder}\n disabled={disabled}\n onChange={(e) => setDraft(e.currentTarget.value)}\n onBlur={() => onCommit(draft)}\n />\n </div>\n </div>\n )\n}\n\n/** 一行「逗号/换行分隔」的字符串列表(失焦提交为数组)。 */\nfunction ListRow(props: {\n label: string\n description?: string\n values: string[]\n placeholder?: string\n disabled: boolean\n onCommit: (_next: string[]) => void\n}): JSX.Element {\n const { label, description, values, placeholder, disabled, onCommit } = props\n const joined = values.join(', ')\n const [draft, setDraft] = useState(joined)\n useEffect(() => {\n setDraft(joined)\n }, [joined])\n return (\n <div className=\"sgRow\">\n <div className=\"sgRowText sgRowText-wide\">\n <div className=\"sgTitle\">{label}</div>\n {description !== undefined && <div className=\"sgDesc\">{description}</div>}\n <input\n className=\"sgInput\"\n type=\"text\"\n value={draft}\n placeholder={placeholder}\n disabled={disabled}\n onChange={(e) => setDraft(e.currentTarget.value)}\n onBlur={() => {\n const next = draft\n .split(/[,\\n]/)\n .map((s) => s.trim())\n .filter((s) => s !== '')\n onCommit(next)\n }}\n />\n </div>\n </div>\n )\n}\n\n/** 一行下拉选择。 */\nfunction SelectRow<T extends string>(props: {\n label: string\n description?: string\n value: T\n options: readonly { value: T; label: string }[]\n disabled: boolean\n onChange: (_next: T) => void\n}): JSX.Element {\n const { label, description, value, options, disabled, onChange } = props\n return (\n <div className=\"sgRow\">\n <div className=\"sgRowText\">\n <div className=\"sgTitle\">{label}</div>\n {description !== undefined && <div className=\"sgDesc\">{description}</div>}\n </div>\n <select\n className=\"sgSelect\"\n value={value}\n disabled={disabled}\n onChange={(e) => onChange(e.currentTarget.value as T)}\n >\n {options.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n </div>\n )\n}\n\n/** 插件配置卡片主体:可展开的 <li> + header 按钮 + pill switch 字段体。 */\nexport function SessionGuardCard({ scope }: SettingsCardProps): JSX.Element {\n const snapshot = useSyncExternalStore(\n (listener) => scope.subscribe(listener),\n () => scope.getSnapshot(),\n )\n const unavailable = snapshot.status === 'unavailable'\n const readonly = unavailable || !snapshot.writable\n const value = (snapshot.value ?? {}) as Partial<SessionGuardConfig>\n const [open, setOpen] = useState(false)\n\n injectCss()\n\n const toggle = (field: keyof SessionGuardConfig, next: boolean) => { void scope.set(field, next) }\n return (\n <li className={'sgCard' + (open ? ' sgCard-open' : '')}>\n <button\n type=\"button\"\n className=\"sgCardHeader\"\n aria-expanded={open}\n onClick={() => setOpen(!open)}\n >\n <span className=\"sgCardHeadtext\">\n <span className=\"sgCardName\">会话守护门禁</span>\n <span className=\"sgCardDesc\">高峰自动暂停运行会话,周末模式无视峰谷畅快跑</span>\n </span>\n <svg\n className={'sgCardChevron' + (open ? ' sgCardChevron-open' : '')}\n viewBox=\"0 0 14 14\"\n width={14}\n height={14}\n fill=\"none\"\n aria-hidden=\"true\"\n >\n <path d=\"M3.5 5.5L7 9l3.5-3.5\" stroke=\"currentColor\" strokeWidth={1.5} strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n </button>\n {open && (\n <div className=\"sgCardBody\">\n {unavailable ? (\n <p style={{ margin: '0', padding: '12px 0', fontSize: '13px', color: 'var(--dsw-alias-label-tertiary)' }}>\n 设置命名空间不可用:请确认 dsh-session-guard 已装配进此 profile。\n </p>\n ) : (\n <>\n <SwitchRow\n label=\"高峰自动暂停冻结会话\"\n description=\"高峰时段自动暂停运行会话\"\n checked={value.enabled ?? true}\n disabled={readonly}\n onChange={(next) => toggle('enabled', next)}\n />\n <SwitchRow\n label=\"官方源二维判定\"\n description=\"高峰期只拦 DeepSeek 官方源;本地/第三方 provider 照常跑\"\n checked={value.providerGuard ?? true}\n disabled={readonly}\n onChange={(next) => toggle('providerGuard', next)}\n />\n <ListRow\n label=\"追加官方 provider id\"\n description=\"精确匹配,优先级最高(逗号分隔)\"\n values={value.officialProviders ?? []}\n placeholder=\"deepseek-official\"\n disabled={readonly}\n onCommit={(next) => { void scope.set('officialProviders', next) }}\n />\n <ListRow\n label=\"官方端点名单\"\n description=\"baseURL 归一化后的 host(逗号分隔)\"\n values={value.officialBaseURLs ?? []}\n placeholder=\"api.deepseek.com\"\n disabled={readonly}\n onCommit={(next) => { void scope.set('officialBaseURLs', next) }}\n />\n <SelectRow<'hold' | 'error'>\n label=\"拦截方式\"\n description=\"挂起等待不报错;或报错并记入延后队列\"\n value={value.deferredMode ?? 'hold'}\n options={[\n { value: 'hold', label: '挂起等待(退峰自动放行)' },\n { value: 'error', label: '报错并延后(退峰续跑)' },\n ]}\n disabled={readonly}\n onChange={(next) => { void scope.set('deferredMode', next) }}\n />\n <SwitchRow\n label=\"退峰自动继续\"\n description=\"关闭后延后的请求/会话不自动续跑,需手动 /resume\"\n checked={value.deferredResume ?? true}\n disabled={readonly}\n onChange={(next) => toggle('deferredResume', next)}\n />\n <TextRow\n label=\"退峰续跑文案\"\n description=\"error 模式退峰时发送的消息内容\"\n value={value.deferredResumeText ?? ''}\n placeholder=\"继续(高峰已过,自动继续)\"\n disabled={readonly}\n onCommit={(next) => { void scope.set('deferredResumeText', next) }}\n />\n <SwitchRow\n label=\"纳入子代理请求\"\n description=\"子代理请求同样计费,默认一并拦截\"\n checked={value.guardSubagents ?? true}\n disabled={readonly}\n onChange={(next) => toggle('guardSubagents', next)}\n />\n <SwitchRow\n label=\"低谷自动恢复\"\n description=\"低峰时段自动恢复被暂停的会话\"\n checked={value.offPeakAutoResume ?? true}\n disabled={readonly}\n onChange={(next) => toggle('offPeakAutoResume', next)}\n />\n <SwitchRow\n label=\"周末模式\"\n description=\"识别周末,无视峰谷畅快跑\"\n checked={value.weekendMode ?? true}\n disabled={readonly}\n onChange={(next) => toggle('weekendMode', next)}\n />\n <SwitchRow\n label=\"回退锁队列\"\n description=\"无会话门时锁等待队列\"\n checked={value.queueFallback ?? true}\n disabled={readonly}\n onChange={(next) => toggle('queueFallback', next)}\n />\n <SwitchRow\n label=\"自动重试\"\n description=\"后端重试,默认关(保守)\"\n checked={value.retryEnabled ?? false}\n disabled={readonly}\n onChange={(next) => toggle('retryEnabled', next)}\n />\n <p className=\"sgHint\">\n 判定口径:显式 id 名单 → baseURL 端点 → catalog 默认端点 → 内置 id。\n 排查误判访问 /session-guard/provider?provider=<id> 看 matchedBy。\n </p>\n {!snapshot.writable && <p className=\"sgReadonly\">当前只读,无法修改。</p>}\n </>\n )}\n </div>\n )}\n </li>\n )\n}\n","/**\n * dsh-session-guard — 浏览器 half。\n *\n * 职责(全部 fail-open,D8):\n * - 在 composer 输入区右侧注册一个**纯展示**状态徽标(高峰/谷时/周末),轮询\n * /session-guard/status;\n * - 注册 `settings.plugin.item` 设置卡片,经 `settingsScope.bind({ namespace })`\n * 绑定 host 已注册的 `session-guard` 命名空间——这正是“插件配置”面板显示本\n * 插件的**必要**机制(对齐 dsh-thinking-levels / dsh-context);\n * - **不做**冻结/会话动作——冻结按钮由 input-traffic 接管并经 /session-guard/rpc\n * 桥接 host 会话门;本插件客户端不注册任何按钮,避免与 input-traffic 冲突。\n *\n * 构建:tsdown → lib/client.js(__ModuleLoader__.load 注册,与 input-traffic 同构)。\n */\nimport { StatusBadge } from './status-badge'\nimport { SessionGuardCard } from './settings-card'\n\n/** 客户端所需服务:slots(状态徽标 + 设置卡片)+ locale + settingsScope(设置卡片绑定)。 */\nexport const inject = ['slots', 'locale', 'settingsScope']\n\n/** 轻量 ctx 类型(仅本客户端用到的方法;构建时类型被剥离)。 */\ninterface SlotsFace {\n inject: (_name: string, _fn: () => unknown) => () => void\n register: (_options: Record<string, unknown>, _component: unknown) => () => void\n}\ninterface SettingsScopeFace {\n bind: (_spec: { namespace: string }) => unknown\n}\ninterface ClientCtx {\n slots: SlotsFace\n settingsScope: SettingsScopeFace\n}\n\n/** host 侧 src/settings.js 注册的命名空间(保持一致)。 */\nconst NS = 'session-guard'\n\nexport function apply(ctx: ClientCtx) {\n // 状态徽标:输入区右侧,纯展示。\n ctx.slots.inject('conversation.input.right', () => ctx.slots.register({\n name: 'conversation.input.right',\n id: 'session-guard-status',\n order: 50,\n locale: 'session-guard',\n }, StatusBadge))\n\n // 插件配置卡片:绑定 session-guard 命名空间,经 settingsScope 渲染并提交。\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({\n name: 'settings.plugin.item',\n id: NS,\n key: NS,\n locale: 'session-guard',\n inject: () => ({\n scope: ctx.settingsScope.bind({ namespace: NS }),\n }),\n }, SessionGuardCard))\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,79 +1,102 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
2
|
+
"name": "dsh-session-guard",
|
|
3
|
+
"version": "0.1.5-beta.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "高峰自动会话门:周末模式 + 高峰自动暂停 + 官方源二维判定 + 会话级冻结 + 后端自动重试。",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"deepseek-harness",
|
|
8
|
+
"dsh",
|
|
9
|
+
"plugin",
|
|
10
|
+
"peak",
|
|
11
|
+
"off-peak",
|
|
12
|
+
"weekend",
|
|
13
|
+
"pause",
|
|
14
|
+
"freeze",
|
|
15
|
+
"retry",
|
|
16
|
+
"session-gate"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/drscrewdriver/session-guard.git"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/drscrewdriver/session-guard",
|
|
24
|
+
"main": "src/index.js",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": "./src/index.js",
|
|
27
|
+
"./client": "./lib/client.js",
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"src/",
|
|
32
|
+
"lib/",
|
|
33
|
+
"cordis.patch.yml",
|
|
34
|
+
"dsh.plugin.json",
|
|
35
|
+
"README.md",
|
|
36
|
+
"README.en.md",
|
|
37
|
+
"README.ja.md",
|
|
38
|
+
"README.ko.md",
|
|
39
|
+
"INSTALL.md",
|
|
40
|
+
"INSTALL.zh.md",
|
|
41
|
+
"INSTALL.ja.md",
|
|
42
|
+
"INSTALL.ko.md",
|
|
43
|
+
"CHANGELOG.md",
|
|
44
|
+
"CHANGELOG.ja.md",
|
|
45
|
+
"CHANGELOG.ko.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"dsh": {
|
|
49
|
+
"client": {
|
|
50
|
+
"inject": [
|
|
51
|
+
"@deepseek-ai/dsh-client-locale",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-settings",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-renderer"
|
|
54
|
+
],
|
|
55
|
+
"platform": "web"
|
|
56
|
+
},
|
|
57
|
+
"bundle": {
|
|
58
|
+
"patch": "./cordis.patch.yml"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsdown",
|
|
63
|
+
"lint": "eslint src tests",
|
|
64
|
+
"test": "node --test \"tests/*.test.mjs\""
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=20",
|
|
68
|
+
"dsh": ">=0.1.0-rc.7 <0.2.0-0"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
75
|
+
"@deepseek-ai/dsh-client-locale": ">=0.1.0-rc.7 <0.2.0-0",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.0-rc.7 <0.2.0-0",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.0-rc.7 <0.2.0-0"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"@deepseek-ai/cordis": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"@deepseek-ai/dsh-client-locale": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-settings": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-slots": {
|
|
90
|
+
"optional": true
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"devDependencies": {
|
|
94
|
+
"@eslint/js": "^10.0.1",
|
|
95
|
+
"@types/react": "~18.3.1",
|
|
96
|
+
"eslint": "^10.8.1",
|
|
97
|
+
"react": "^18.2.0",
|
|
98
|
+
"tsdown": "^0.22.2",
|
|
99
|
+
"typescript": "^5.6.0",
|
|
100
|
+
"typescript-eslint": "^8.67.0"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-session-guard — 状态徽标文案(纯函数,零依赖,可单测)。
|
|
3
|
+
*
|
|
4
|
+
* 分离出来的原因:徽标语义(高峰期「只拦官方」vs「全部暂停」)需要单测,
|
|
5
|
+
* 而组件文件是 TSX(含 React 运行时)。这里只有字符串逻辑。
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** /session-guard/status 返回的全局阶段(组件与测试共用)。 */
|
|
9
|
+
export interface Status {
|
|
10
|
+
phase: 'peak' | 'off-peak' | 'weekend'
|
|
11
|
+
enabled: boolean
|
|
12
|
+
weekendMode: boolean
|
|
13
|
+
/** 官方源二维判定是否开启(开启时高峰期只拦官方源)。 */
|
|
14
|
+
providerGuard?: boolean
|
|
15
|
+
/** 当前被挂起的请求数(hold 模式)。 */
|
|
16
|
+
held?: number
|
|
17
|
+
/** error 模式记下的延后会话数。 */
|
|
18
|
+
deferred?: number
|
|
19
|
+
timezone: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 阶段文案。 */
|
|
23
|
+
export const PHASE_LABELS = {
|
|
24
|
+
peak: '高峰',
|
|
25
|
+
'off-peak': '谷时',
|
|
26
|
+
weekend: '周末',
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
/** 高峰期的徽标文案:二维判定开启时区分「只拦官方」与「全部暂停」。 */
|
|
30
|
+
export function peakLabel(status: Pick<Status, 'phase' | 'providerGuard'>): string {
|
|
31
|
+
if (status.phase !== 'peak') return PHASE_LABELS[status.phase]
|
|
32
|
+
return status.providerGuard === true ? '高峰·拦官方' : '高峰·全部暂停'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 悬浮说明:把判定口径与当前挂起/延后数量写清楚。 */
|
|
36
|
+
export function badgeTitle(status: Status): string {
|
|
37
|
+
const base = `${PHASE_LABELS[status.phase]} · ${status.timezone}${status.weekendMode ? ' · 周末模式' : ''}`
|
|
38
|
+
if (status.phase !== 'peak') return base
|
|
39
|
+
const mode = status.providerGuard === true ? '仅拦截 DeepSeek 官方源' : '全部会话暂停(未启用二维判定)'
|
|
40
|
+
const held = status.held ?? 0
|
|
41
|
+
const deferred = status.deferred ?? 0
|
|
42
|
+
const extra = held > 0 || deferred > 0 ? ` · 挂起 ${held} · 延后 ${deferred}` : ''
|
|
43
|
+
return `${base} · ${mode}${extra}`
|
|
44
|
+
}
|
package/src/client/locales.ts
CHANGED
|
@@ -1,95 +1,163 @@
|
|
|
1
|
-
/** `session-guard` client dictionaries (zh / en / ja / ko). */
|
|
2
|
-
|
|
3
|
-
/** Dictionary namespace owned by this plugin. */
|
|
4
|
-
export const NS = 'session-guard'
|
|
5
|
-
|
|
6
|
-
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
7
|
-
export const zh = {
|
|
8
|
-
'badge.normal': '正常',
|
|
9
|
-
'badge.paused': '已暂停(峰值)',
|
|
10
|
-
'badge_paused_peak': '峰值暂停中',
|
|
11
|
-
'settings.enabled': '高峰自动暂停',
|
|
12
|
-
'settings.enabled.desc': '高峰时段自动暂停运行中的会话',
|
|
13
|
-
'settings.offPeakAutoResume': '低谷自动恢复',
|
|
14
|
-
'settings.offPeakAutoResume.desc': '低峰时段自动恢复被暂停的会话',
|
|
15
|
-
'settings.weekendMode': '周末模式',
|
|
16
|
-
'settings.weekendMode.desc': '识别周末 → 周末不自动暂停',
|
|
17
|
-
'settings.queueFallback': '回退锁等待队列',
|
|
18
|
-
'settings.queueFallback.desc': '自研会话门不可用时回退锁等待队列(fail-open)',
|
|
19
|
-
'settings.retryEnabled': '自动重试',
|
|
20
|
-
'settings.retryEnabled.desc': '瞬时失败自动续跑(默认关,保守)',
|
|
21
|
-
'settings.timezone': '时区',
|
|
22
|
-
'settings.peakWindows': '高峰时间窗口',
|
|
23
|
-
'settings.
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'settings.
|
|
34
|
-
'settings.
|
|
35
|
-
'settings.
|
|
36
|
-
'settings.
|
|
37
|
-
'settings.
|
|
38
|
-
'settings.
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'settings.
|
|
56
|
-
'settings.
|
|
57
|
-
'settings.
|
|
58
|
-
'settings.
|
|
59
|
-
'settings.
|
|
60
|
-
'settings.
|
|
61
|
-
'settings.
|
|
62
|
-
'settings.
|
|
63
|
-
'settings.
|
|
64
|
-
'settings.
|
|
65
|
-
'settings.
|
|
66
|
-
'settings.
|
|
67
|
-
'settings.
|
|
68
|
-
'
|
|
69
|
-
'
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
'
|
|
75
|
-
'
|
|
76
|
-
'
|
|
77
|
-
'settings.
|
|
78
|
-
'
|
|
79
|
-
'
|
|
80
|
-
'
|
|
81
|
-
'
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
'
|
|
87
|
-
'
|
|
88
|
-
'
|
|
89
|
-
'settings.
|
|
90
|
-
'
|
|
91
|
-
'
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
1
|
+
/** `session-guard` client dictionaries (zh / en / ja / ko). */
|
|
2
|
+
|
|
3
|
+
/** Dictionary namespace owned by this plugin. */
|
|
4
|
+
export const NS = 'session-guard'
|
|
5
|
+
|
|
6
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
7
|
+
export const zh = {
|
|
8
|
+
'badge.normal': '正常',
|
|
9
|
+
'badge.paused': '已暂停(峰值)',
|
|
10
|
+
'badge_paused_peak': '峰值暂停中',
|
|
11
|
+
'settings.enabled': '高峰自动暂停',
|
|
12
|
+
'settings.enabled.desc': '高峰时段自动暂停运行中的会话',
|
|
13
|
+
'settings.offPeakAutoResume': '低谷自动恢复',
|
|
14
|
+
'settings.offPeakAutoResume.desc': '低峰时段自动恢复被暂停的会话',
|
|
15
|
+
'settings.weekendMode': '周末模式',
|
|
16
|
+
'settings.weekendMode.desc': '识别周末 → 周末不自动暂停',
|
|
17
|
+
'settings.queueFallback': '回退锁等待队列',
|
|
18
|
+
'settings.queueFallback.desc': '自研会话门不可用时回退锁等待队列(fail-open)',
|
|
19
|
+
'settings.retryEnabled': '自动重试',
|
|
20
|
+
'settings.retryEnabled.desc': '瞬时失败自动续跑(默认关,保守)',
|
|
21
|
+
'settings.timezone': '时区',
|
|
22
|
+
'settings.peakWindows': '高峰时间窗口',
|
|
23
|
+
'settings.providerGuard': '官方源二维判定',
|
|
24
|
+
'settings.providerGuard.desc': '高峰期只拦 DeepSeek 官方源;本地/第三方 provider 照常跑',
|
|
25
|
+
'settings.officialProviders': '追加官方 provider id',
|
|
26
|
+
'settings.officialProviders.desc': '精确匹配,优先级最高(逗号分隔)',
|
|
27
|
+
'settings.officialBaseURLs': '官方端点名单',
|
|
28
|
+
'settings.officialBaseURLs.desc': 'baseURL 归一化后的 host(逗号分隔)',
|
|
29
|
+
'settings.deferredMode': '拦截方式',
|
|
30
|
+
'settings.deferredMode.desc': '挂起等待不报错;或报错并记入延后队列',
|
|
31
|
+
'settings.deferredResume': '退峰自动继续',
|
|
32
|
+
'settings.deferredResume.desc': '关闭后延后的请求/会话不自动续跑,需手动 /resume',
|
|
33
|
+
'settings.deferredResumeText': '退峰续跑文案',
|
|
34
|
+
'settings.deferredResumeText.desc': 'error 模式退峰时发送的消息内容',
|
|
35
|
+
'settings.guardSubagents': '纳入子代理请求',
|
|
36
|
+
'settings.guardSubagents.desc': '子代理请求同样计费,默认一并拦截',
|
|
37
|
+
'settings.officialHint': '判定口径:显式 id 名单 → baseURL 端点 → catalog 默认端点 → 内置 id。排查误判访问 /session-guard/provider?provider=<id> 看 matchedBy。',
|
|
38
|
+
'settings.unavailable': '设置不可用:请确认 dsh-session-guard 已装配进 profile。',
|
|
39
|
+
'status.normal': '正常运行',
|
|
40
|
+
'status.paused_peak': '峰值暂停中',
|
|
41
|
+
'status.peak_official': '高峰·拦官方',
|
|
42
|
+
'status.peak_all': '高峰·全部暂停',
|
|
43
|
+
} satisfies Record<string, string>
|
|
44
|
+
|
|
45
|
+
/** English dictionary (keys mirror zh). */
|
|
46
|
+
export const en: Record<keyof typeof zh, string> = {
|
|
47
|
+
'badge.normal': 'Normal',
|
|
48
|
+
'badge.paused': 'Paused (peak)',
|
|
49
|
+
'badge_paused_peak': 'Peak paused',
|
|
50
|
+
'settings.enabled': 'Peak auto-pause',
|
|
51
|
+
'settings.enabled.desc': 'Auto-pause running sessions during peak hours',
|
|
52
|
+
'settings.offPeakAutoResume': 'Off-peak auto-resume',
|
|
53
|
+
'settings.offPeakAutoResume.desc': 'Auto-resume paused sessions during off-peak hours',
|
|
54
|
+
'settings.weekendMode': 'Weekend mode',
|
|
55
|
+
'settings.weekendMode.desc': 'Detect weekends → no auto-pause on weekends',
|
|
56
|
+
'settings.queueFallback': 'Fallback lock-wait queue',
|
|
57
|
+
'settings.queueFallback.desc': 'Fallback to lock-wait queue when custom session gate is unavailable (fail-open)',
|
|
58
|
+
'settings.retryEnabled': 'Auto-retry',
|
|
59
|
+
'settings.retryEnabled.desc': 'Transient failure auto-resume (off by default, conservative)',
|
|
60
|
+
'settings.timezone': 'Timezone',
|
|
61
|
+
'settings.peakWindows': 'Peak time windows',
|
|
62
|
+
'settings.providerGuard': 'Official-source guard',
|
|
63
|
+
'settings.providerGuard.desc': 'During peak hours only DeepSeek official sources are blocked; local/third-party providers keep running',
|
|
64
|
+
'settings.officialProviders': 'Extra official provider ids',
|
|
65
|
+
'settings.officialProviders.desc': 'Exact match, highest priority (comma separated)',
|
|
66
|
+
'settings.officialBaseURLs': 'Official endpoint hosts',
|
|
67
|
+
'settings.officialBaseURLs.desc': 'Hosts after baseURL normalization (comma separated)',
|
|
68
|
+
'settings.deferredMode': 'Interception mode',
|
|
69
|
+
'settings.deferredMode.desc': 'Hold without error, or fail and queue as deferred',
|
|
70
|
+
'settings.deferredResume': 'Auto-resume after peak',
|
|
71
|
+
'settings.deferredResume.desc': 'When off, deferred requests/sessions stay parked until a manual /resume',
|
|
72
|
+
'settings.deferredResumeText': 'Resume message',
|
|
73
|
+
'settings.deferredResumeText.desc': 'Text sent when an error-mode session resumes off-peak',
|
|
74
|
+
'settings.guardSubagents': 'Guard subagent requests',
|
|
75
|
+
'settings.guardSubagents.desc': 'Subagent requests are billed too; guarded by default',
|
|
76
|
+
'settings.officialHint': 'Decision order: explicit id list → baseURL endpoint → catalog default endpoint → builtin id. For a wrong verdict open /session-guard/provider?provider=<id> and read matchedBy.',
|
|
77
|
+
'settings.unavailable': 'Settings unavailable: make sure dsh-session-guard is assembled into this profile.',
|
|
78
|
+
'status.normal': 'Running normally',
|
|
79
|
+
'status.paused_peak': 'Peak paused',
|
|
80
|
+
'status.peak_official': 'Peak · official only',
|
|
81
|
+
'status.peak_all': 'Peak · all paused',
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Japanese dictionary (keys mirror zh). */
|
|
85
|
+
export const ja: Record<keyof typeof zh, string> = {
|
|
86
|
+
'badge.normal': '通常',
|
|
87
|
+
'badge.paused': '一時停止(ピーク)',
|
|
88
|
+
'badge_paused_peak': 'ピーク一時停止中',
|
|
89
|
+
'settings.enabled': 'ピーク自動一時停止',
|
|
90
|
+
'settings.enabled.desc': 'ピーク時間帯に実行中セッションを自動一時停止',
|
|
91
|
+
'settings.offPeakAutoResume': 'オフピーク自動再開',
|
|
92
|
+
'settings.offPeakAutoResume.desc': 'オフピーク時に一時停止セッションを自動再開',
|
|
93
|
+
'settings.weekendMode': '週末モード',
|
|
94
|
+
'settings.weekendMode.desc': '週末を認識→週末は自動一時停止しない',
|
|
95
|
+
'settings.queueFallback': 'フォールバックロック待機キュー',
|
|
96
|
+
'settings.queueFallback.desc': 'カスタムセッションゲート利用不可時にロック待機キューにフォールバック(fail-open)',
|
|
97
|
+
'settings.retryEnabled': '自動リトライ',
|
|
98
|
+
'settings.retryEnabled.desc': '瞬時失敗自動再試行(デフォルトオフ、保守的)',
|
|
99
|
+
'settings.timezone': 'タイムゾーン',
|
|
100
|
+
'settings.peakWindows': 'ピーク時間ウィンドウ',
|
|
101
|
+
'settings.providerGuard': '公式ソース二次判定',
|
|
102
|
+
'settings.providerGuard.desc': 'ピーク時は DeepSeek 公式ソースのみ停止。ローカル/第三者 provider は通常どおり実行',
|
|
103
|
+
'settings.officialProviders': '公式 provider id を追加',
|
|
104
|
+
'settings.officialProviders.desc': '完全一致、最優先(カンマ区切り)',
|
|
105
|
+
'settings.officialBaseURLs': '公式エンドポイント host',
|
|
106
|
+
'settings.officialBaseURLs.desc': 'baseURL 正規化後の host(カンマ区切り)',
|
|
107
|
+
'settings.deferredMode': '遮断方式',
|
|
108
|
+
'settings.deferredMode.desc': 'エラーなしで保留、またはエラーにして延後キューへ',
|
|
109
|
+
'settings.deferredResume': 'ピーク後の自動再開',
|
|
110
|
+
'settings.deferredResume.desc': 'オフにすると延後したリクエスト/セッションは手動 /resume まで停止',
|
|
111
|
+
'settings.deferredResumeText': '再開メッセージ',
|
|
112
|
+
'settings.deferredResumeText.desc': 'error モードでピーク後に送るテキスト',
|
|
113
|
+
'settings.guardSubagents': 'サブエージェントも対象',
|
|
114
|
+
'settings.guardSubagents.desc': 'サブエージェントのリクエストも課金対象。既定で遮断',
|
|
115
|
+
'settings.officialHint': '判定順:明示 id リスト → baseURL エンドポイント → catalog 既定エンドポイント → 内蔵 id。誤判定は /session-guard/provider?provider=<id> の matchedBy を確認。',
|
|
116
|
+
'settings.unavailable': '設定利用不可:dsh-session-guard が profile に組み込まれているか確認してください。',
|
|
117
|
+
'status.normal': '通常実行中',
|
|
118
|
+
'status.paused_peak': 'ピーク一時停止中',
|
|
119
|
+
'status.peak_official': 'ピーク・公式のみ',
|
|
120
|
+
'status.peak_all': 'ピーク・全停止',
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Korean dictionary (keys mirror zh). */
|
|
124
|
+
export const ko: Record<keyof typeof zh, string> = {
|
|
125
|
+
'badge.normal': '정상',
|
|
126
|
+
'badge.paused': '일시정지(피크)',
|
|
127
|
+
'badge_paused_peak': '피크 일시정지 중',
|
|
128
|
+
'settings.enabled': '피크 자동 일시정지',
|
|
129
|
+
'settings.enabled.desc': '피크 시간대에 실행 중인 세션을 자동 일시정지',
|
|
130
|
+
'settings.offPeakAutoResume': '오피크 자동 재개',
|
|
131
|
+
'settings.offPeakAutoResume.desc': '오피크 시간대에 일시정지된 세션을 자동 재개',
|
|
132
|
+
'settings.weekendMode': '주말 모드',
|
|
133
|
+
'settings.weekendMode.desc': '주말 감지 → 주말에는 자동 일시정지 안 함',
|
|
134
|
+
'settings.queueFallback': '폴백 잠금 대기 큐',
|
|
135
|
+
'settings.queueFallback.desc': '커스텀 세션 게이트 사용 불가 시 잠금 대기 큐로 폴백(fail-open)',
|
|
136
|
+
'settings.retryEnabled': '자동 재시도',
|
|
137
|
+
'settings.retryEnabled.desc': '일시적 실패 자동 재개(기본값 꺼짐, 보수적)',
|
|
138
|
+
'settings.timezone': '타임존',
|
|
139
|
+
'settings.peakWindows': '피크 시간대',
|
|
140
|
+
'settings.providerGuard': '공식 소스 2차 판정',
|
|
141
|
+
'settings.providerGuard.desc': '피크 시간에는 DeepSeek 공식 소스만 차단, 로컬/서드파티 provider는 정상 실행',
|
|
142
|
+
'settings.officialProviders': '공식 provider id 추가',
|
|
143
|
+
'settings.officialProviders.desc': '정확히 일치, 최우선(쉼표 구분)',
|
|
144
|
+
'settings.officialBaseURLs': '공식 엔드포인트 host',
|
|
145
|
+
'settings.officialBaseURLs.desc': 'baseURL 정규화 후 host(쉼표 구분)',
|
|
146
|
+
'settings.deferredMode': '차단 방식',
|
|
147
|
+
'settings.deferredMode.desc': '오류 없이 보류, 또는 오류로 연기 후 큐에 기록',
|
|
148
|
+
'settings.deferredResume': '피크 후 자동 재개',
|
|
149
|
+
'settings.deferredResume.desc': '끄면 보류된 요청/세션은 수동 /resume 전까지 멈춤',
|
|
150
|
+
'settings.deferredResumeText': '재개 메시지',
|
|
151
|
+
'settings.deferredResumeText.desc': 'error 모드에서 피크 후 보내는 텍스트',
|
|
152
|
+
'settings.guardSubagents': '서브에이전트 포함',
|
|
153
|
+
'settings.guardSubagents.desc': '서브에이전트 요청도 과금 대상, 기본 포함',
|
|
154
|
+
'settings.officialHint': '판정 순서: 명시 id 목록 → baseURL 엔드포인트 → catalog 기본 엔드포인트 → 내장 id. 오판정은 /session-guard/provider?provider=<id> 의 matchedBy 확인.',
|
|
155
|
+
'settings.unavailable': '설정 사용 불가: dsh-session-guard가 이 profile에 조립되었는지 확인하세요.',
|
|
156
|
+
'status.normal': '정상 실행 중',
|
|
157
|
+
'status.paused_peak': '피크 일시정지 중',
|
|
158
|
+
'status.peak_official': '피크·공식만',
|
|
159
|
+
'status.peak_all': '피크·전체 정지',
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Dictionary key union. */
|
|
163
|
+
export type SessionGuardKey = keyof typeof zh
|