dsh-router-core 0.4.7 → 0.4.8
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 +9 -69
- package/cordis.patch.yml +2 -7
- package/docs/ext.md +3 -17
- package/lib/client-registry.js +50 -565
- package/lib/client.js +50 -565
- package/lib/ext-prompt/index.js +20 -15
- package/lib/index.js +0 -419
- package/lib/types/ext/contract.d.ts +1 -1
- package/lib/types/ext-prompt/content.d.ts +11 -0
- package/lib/types/ext-prompt/index.d.ts +7 -9
- package/lib/types/ext-prompt/mount.d.ts +2 -2
- package/lib/types/ext-prompt/plugin.d.ts +3 -5
- package/lib/types/suppliers/registry.d.ts +0 -1
- package/package.json +1 -10
- package/src/client/ExtDetail.tsx +1 -13
- package/src/client/group-controls.test.ts +1 -1
- package/src/client/group-controls.ts +1 -1
- package/src/client/index.tsx +0 -17
- package/src/client/router-components.test.ts +1 -1
- package/src/client/router.css +3 -354
- package/src/ext/contract.ts +1 -1
- package/src/ext-prompt/content.ts +17 -12
- package/src/ext-prompt/ext-prompt.test.ts +3 -3
- package/src/ext-prompt/index.ts +7 -9
- package/src/ext-prompt/mount.ts +2 -2
- package/src/ext-prompt/plugin.ts +3 -5
- package/src/router/index.ts +2 -3
- package/src/router/models-cache.test.ts +5 -6
- package/src/router/supplier-switch.test.ts +1 -1
- package/src/suppliers/registry.ts +0 -71
- package/lib/contract-D4BWZ7Xe.js +0 -13
- package/lib/ext-test/index.js +0 -36
- package/lib/ext-test/locale/en.json +0 -6
- package/lib/ext-test/locale/zh.json +0 -6
- package/lib/ext-test/package.json +0 -5
- package/lib/types/ext-test/index.d.ts +0 -21
- package/lib/types/ext-test/plugin.d.ts +0 -23
- package/lib/types/suppliers/probe.d.ts +0 -160
- package/src/client/ExtTestPanel.tsx +0 -392
- package/src/client/comp-button-scope.test.ts +0 -58
- package/src/client/comp-panel-layout.test.ts +0 -58
- package/src/client/ext-panels.test.ts +0 -44
- package/src/client/ext-panels.ts +0 -49
- package/src/client/model-choice.test.ts +0 -41
- package/src/client/model-choice.ts +0 -37
- package/src/ext-test/ext-test.test.ts +0 -60
- package/src/ext-test/index.ts +0 -35
- package/src/ext-test/locale/en.json +0 -6
- package/src/ext-test/locale/zh.json +0 -6
- package/src/ext-test/plugin.ts +0 -34
- package/src/ext-test/row.json +0 -5
- package/src/suppliers/probe.test.ts +0 -266
- package/src/suppliers/probe.ts +0 -475
package/src/client/ExtDetail.tsx
CHANGED
|
@@ -13,13 +13,11 @@
|
|
|
13
13
|
*
|
|
14
14
|
* 列表页只列**已启用**的扩展(关闭的连卡片都不出现),所以这里也不必再显示
|
|
15
15
|
* 「已启用/未启用」——那行字只能是常量。布局同供应商详情:返回链接 + 图标 +
|
|
16
|
-
*
|
|
17
|
-
* 按需加独立面板,此处不内置任何具体扩展的专属视图。
|
|
16
|
+
* 名称/副行,下面跟扩展说明卡与未就绪横幅。
|
|
18
17
|
*/
|
|
19
18
|
import { Fragment, useEffect, useState } from 'react'
|
|
20
19
|
import { Switch } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
21
20
|
import { ROUTER_API_BASE, type ExtControlItem, type RouterExtItem, type RouterExtResponse } from '../shared.ts'
|
|
22
|
-
import { extPanel } from './ext-panels.ts'
|
|
23
21
|
import { groupControlRows } from './group-controls.ts'
|
|
24
22
|
import { Modal } from './Modal.tsx'
|
|
25
23
|
|
|
@@ -599,10 +597,6 @@ interface ExtDetailProps {
|
|
|
599
597
|
}
|
|
600
598
|
|
|
601
599
|
export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
|
|
602
|
-
// 扩展**自带**详情面板时用它(插件自检就是这么做的:选供应商/模型/连接 + 跑测试)。
|
|
603
|
-
// 没有就退回下面这张通用只读页 —— 保留返回链接与标题,导航形状与有没有自定义
|
|
604
|
-
// 面板无关,用户点进去的预期是稳定的。
|
|
605
|
-
const Custom = extPanel(item.id)
|
|
606
600
|
return (
|
|
607
601
|
<div className="dshr-tabBody">
|
|
608
602
|
{/* Header(同供应商详情:返回链接 + 图标 + 名称/副行) */}
|
|
@@ -635,13 +629,9 @@ export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
|
|
|
635
629
|
</div>
|
|
636
630
|
</div>
|
|
637
631
|
|
|
638
|
-
{/* 扩展自带的面板优先(注册表命中时下面几块都不渲染) */}
|
|
639
|
-
{Custom !== undefined ? <Custom /> : (
|
|
640
|
-
<>
|
|
641
632
|
{/* 子开关(扩展自己的行为细节;没有 controls 就不渲染这一块) */}
|
|
642
633
|
{item.controls !== undefined && item.controls.length > 0 && <ExtControls item={item} />}
|
|
643
634
|
|
|
644
|
-
{/* 实际生效的提示词(只读预览;回答「关掉的真的没进去吗」) */}
|
|
645
635
|
{/* 未就绪警示(同供应商详情的出错横幅) */}
|
|
646
636
|
{item.ready === false && (
|
|
647
637
|
<div className="dshr-alert">
|
|
@@ -649,8 +639,6 @@ export function ExtDetail({ item, onBack }: ExtDetailProps): JSX.Element {
|
|
|
649
639
|
<span>{item.detail !== undefined && item.detail !== '' ? item.detail : '当前不可用'}</span>
|
|
650
640
|
</div>
|
|
651
641
|
)}
|
|
652
|
-
</>
|
|
653
|
-
)}
|
|
654
642
|
</div>
|
|
655
643
|
)
|
|
656
644
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* 准则分类 + 宿主系统提示词段落),平铺成一长列无从分辨哪条是谁的。
|
|
6
6
|
*
|
|
7
7
|
* 两条规则:
|
|
8
|
-
* ① **无组节恒排最前** —— 没用 `group`
|
|
8
|
+
* ① **无组节恒排最前** —— 没用 `group` 字段的扩展拿到的
|
|
9
9
|
* 分组结果就是原来的平铺列表(一个无名节),面板行为一个像素不变;
|
|
10
10
|
* ② 组的先后 = 组名在 `rows` 里的**首次出现序**,组内保持原顺序 ——
|
|
11
11
|
* 不按字母重排:`rows` 的顺序来自服务端(段落按 prompt 序),重排
|
package/src/client/index.tsx
CHANGED
|
@@ -11,9 +11,6 @@
|
|
|
11
11
|
import { mountRouterWorkspace } from './workspace-mount.tsx'
|
|
12
12
|
import { RouterSettingsSection } from './settings-section.tsx'
|
|
13
13
|
import { RouterComponentsSection } from './RouterComponentsSection.tsx'
|
|
14
|
-
import { ExtTestPanel } from './ExtTestPanel.tsx'
|
|
15
|
-
import { registerExtPanel } from './ext-panels.ts'
|
|
16
|
-
import { EXT_TEST_ID } from '../ext-test/plugin.ts'
|
|
17
14
|
import { registerSettingsNavIcon } from './settings-nav-icon.ts'
|
|
18
15
|
import { rewriteRouterModelHint } from './model-hint-copy.ts'
|
|
19
16
|
import { LastHitDock } from './LastHitDock.tsx'
|
|
@@ -97,19 +94,6 @@ function mountComponentsSection(ctx: Ctx): (() => void) | undefined {
|
|
|
97
94
|
}, RouterComponentsSection))
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
/**
|
|
101
|
-
* 给「插件自检」扩展登记它的详情面板。
|
|
102
|
-
*
|
|
103
|
-
* 面板的**唯一入口**是设置 → 路由 → 扩展 → 点开那张卡片(`ExtDetail` 查注册表拿到
|
|
104
|
-
* 本组件)。**刻意不给插件页那一行再挂一个详情页**:那一行的宿主行开关已经能开/关
|
|
105
|
-
* 它了,再点进去看同一块面板就是同一个东西有两个入口 —— 与「内置扩展不在自绘节里
|
|
106
|
-
* 重复列」是同一条理由。
|
|
107
|
-
*/
|
|
108
|
-
function registerExtTestPanel(ctx: Ctx): void {
|
|
109
|
-
const dispose = registerExtPanel(EXT_TEST_ID, ExtTestPanel)
|
|
110
|
-
ctx.effect(() => dispose, 'dsh-router: ext test panel registry')
|
|
111
|
-
}
|
|
112
|
-
|
|
113
97
|
export function apply(ctx: Ctx): void {
|
|
114
98
|
// 「最近命中」徽章:走官方 conversation.composer.dock(scope: session → 组件
|
|
115
99
|
// props 注入 sessionId,按当前会话取命中)。0.2.0 起这个座位无条件可用 ——
|
|
@@ -128,7 +112,6 @@ export function apply(ctx: Ctx): void {
|
|
|
128
112
|
if (disposeComponents !== undefined) {
|
|
129
113
|
ctx.effect(() => disposeComponents, 'dsh-router: plugin page components')
|
|
130
114
|
}
|
|
131
|
-
registerExtTestPanel(ctx)
|
|
132
115
|
// 换掉宿主的默认齿轮(契约没有 icon 字段,只能注册后认领自己的行)
|
|
133
116
|
ctx.effect(() => registerSettingsNavIcon(SECTION_LABEL), 'dsh-router: settings nav icon')
|
|
134
117
|
// 设置-模型 里 Router 卡片的那句死路提示 → 正确说法(同样只能认领后就地改写)
|
|
@@ -105,7 +105,7 @@ test('内置扩展不进这一节(它已经是原生「包含的组件」里
|
|
|
105
105
|
const out = groupRouterComponents({ ok: true }, {
|
|
106
106
|
ok: true,
|
|
107
107
|
enhancers: [
|
|
108
|
-
{ id: '
|
|
108
|
+
{ id: 'prompt', name: '分层提示词', source: 'builtin' },
|
|
109
109
|
{ id: 'rtk', name: 'RTK' },
|
|
110
110
|
],
|
|
111
111
|
})
|
package/src/client/router.css
CHANGED
|
@@ -3456,9 +3456,9 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not(
|
|
|
3456
3456
|
}
|
|
3457
3457
|
|
|
3458
3458
|
/* 标题 + 「修改」按钮的**同一行**(`ExtDetail` 的子开关区)。
|
|
3459
|
-
⚠️ 单独一个类而不改 `.dshr-compRowName`:那个类 `RouterComponentsSection`
|
|
3460
|
-
|
|
3461
|
-
flex
|
|
3459
|
+
⚠️ 单独一个类而不改 `.dshr-compRowName`:那个类 `RouterComponentsSection`
|
|
3460
|
+
也在用,给它加 `display:flex` 会把**那一页的标题**也变成
|
|
3461
|
+
flex 盒子(那边标题后面没有按钮,平白多一层)。
|
|
3462
3462
|
没有这层时按钮会掉到**下一行**:`.dshr-compRowMain` 是 column,而
|
|
3463
3463
|
`.dshr-compRowName` 是行内元素、标题一长就折行(2026-09-29 用户报)。 */
|
|
3464
3464
|
/* 子开关区:`.dshr-compGroup` 的 `gap: 12px` 覆盖为 0(2026-09-30 用户要求)。
|
|
@@ -3512,354 +3512,3 @@ html[data-dsh-router-active]:not([data-dsh-taskboard-active]):not(
|
|
|
3512
3512
|
缩进那种「引用块」的额外装饰(用户要求),层级只靠颜色就够。 */
|
|
3513
3513
|
white-space: pre-wrap;
|
|
3514
3514
|
}
|
|
3515
|
-
|
|
3516
|
-
/* ============================================================
|
|
3517
|
-
dsh-router — 插件自检面板(设置 → 路由 → 扩展 → 插件自检)
|
|
3518
|
-
------------------------------------------------------------
|
|
3519
|
-
这一页渲染在**宿主的插件页**里,DOM 祖先既不是 .dshr-settings 也不是
|
|
3520
|
-
.dshr-shell,所以文件顶部那套 `--rs-*` 主题变量在这里**不存在**(未定义的
|
|
3521
|
-
var() 会让整条声明失效:边框/文字色退成继承值)。
|
|
3522
|
-
|
|
3523
|
-
所以这里**不用**设置页那套组件样式,直接按宿主页面的视觉语言写,用宿主的
|
|
3524
|
-
`--dsw-alias-*` token:几何对齐它(14px 小标题、12px 次要文字、0.5px 描边、
|
|
3525
|
-
12px 圆角),配色也从同一套 token 取 —— 于是这块面板看起来就是原生的一部分。
|
|
3526
|
-
主按钮不用在这里画:交给宿主的 Button 原语(见 env.d.ts),配色由原语自己的
|
|
3527
|
-
token 家族决定,不必猜「主色底上配什么字色」。
|
|
3528
|
-
============================================================ */
|
|
3529
|
-
|
|
3530
|
-
.dshr-comp {
|
|
3531
|
-
gap: 16px;
|
|
3532
|
-
}
|
|
3533
|
-
|
|
3534
|
-
.dshr-compIntro {
|
|
3535
|
-
margin: 0;
|
|
3536
|
-
color: var(--dsw-alias-label-secondary);
|
|
3537
|
-
font-size: 13px;
|
|
3538
|
-
line-height: 20px;
|
|
3539
|
-
}
|
|
3540
|
-
|
|
3541
|
-
/* 三栏收进同一块面板,读起来是「一组控件」而不是三个飘着的输入框。
|
|
3542
|
-
窄了换行(详情区宽度随窗口变,写死三列会在窄窗口挤成一团)。 */
|
|
3543
|
-
.dshr-compForm {
|
|
3544
|
-
display: grid;
|
|
3545
|
-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
3546
|
-
gap: 14px 16px;
|
|
3547
|
-
padding: 14px 16px;
|
|
3548
|
-
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
3549
|
-
border-radius: 12px;
|
|
3550
|
-
background: var(--dsw-alias-bg-layer-1);
|
|
3551
|
-
}
|
|
3552
|
-
|
|
3553
|
-
.dshr-compField {
|
|
3554
|
-
display: flex;
|
|
3555
|
-
flex-direction: column;
|
|
3556
|
-
gap: 6px;
|
|
3557
|
-
min-width: 0;
|
|
3558
|
-
}
|
|
3559
|
-
|
|
3560
|
-
.dshr-compFieldHead {
|
|
3561
|
-
display: flex;
|
|
3562
|
-
align-items: baseline;
|
|
3563
|
-
justify-content: space-between;
|
|
3564
|
-
gap: 8px;
|
|
3565
|
-
}
|
|
3566
|
-
|
|
3567
|
-
.dshr-compFieldLabel {
|
|
3568
|
-
color: var(--dsw-alias-label-primary);
|
|
3569
|
-
font-size: 12px;
|
|
3570
|
-
font-weight: 500;
|
|
3571
|
-
line-height: 18px;
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
|
-
/* 标题右侧的用法要点。语义放这里比塞进顶部那段长文字更容易被读到 ——
|
|
3575
|
-
「模型只列可用的」「连接留空 = 池内任选」都是用这一栏时才关心的。 */
|
|
3576
|
-
.dshr-compFieldNote {
|
|
3577
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3578
|
-
font-size: 12px;
|
|
3579
|
-
line-height: 18px;
|
|
3580
|
-
text-align: right;
|
|
3581
|
-
}
|
|
3582
|
-
|
|
3583
|
-
/* 36px / 12px 圆角,与宿主 Button 原语的 md 尺寸对齐。 */
|
|
3584
|
-
.dshr-compSelect {
|
|
3585
|
-
width: 100%;
|
|
3586
|
-
height: 36px;
|
|
3587
|
-
padding: 0 32px 0 11px;
|
|
3588
|
-
appearance: none;
|
|
3589
|
-
border: 0.5px solid var(--dsw-alias-border-l2);
|
|
3590
|
-
border-radius: 12px;
|
|
3591
|
-
background-color: var(--dsw-alias-bg-layer-2);
|
|
3592
|
-
background-image:
|
|
3593
|
-
linear-gradient(45deg, transparent 50%, var(--dsw-alias-label-tertiary) 50%),
|
|
3594
|
-
linear-gradient(135deg, var(--dsw-alias-label-tertiary) 50%, transparent 50%);
|
|
3595
|
-
background-position:
|
|
3596
|
-
calc(100% - 17px) 16px,
|
|
3597
|
-
calc(100% - 12px) 16px;
|
|
3598
|
-
background-size: 5px 5px;
|
|
3599
|
-
background-repeat: no-repeat;
|
|
3600
|
-
color: var(--dsw-alias-label-primary);
|
|
3601
|
-
font: inherit;
|
|
3602
|
-
font-size: 13px;
|
|
3603
|
-
line-height: 18px;
|
|
3604
|
-
text-overflow: ellipsis;
|
|
3605
|
-
cursor: pointer;
|
|
3606
|
-
}
|
|
3607
|
-
|
|
3608
|
-
.dshr-compSelect:hover:not(:disabled) {
|
|
3609
|
-
border-color: var(--dsw-alias-border-l3);
|
|
3610
|
-
}
|
|
3611
|
-
|
|
3612
|
-
.dshr-compSelect:focus {
|
|
3613
|
-
outline: none;
|
|
3614
|
-
border-color: var(--dsw-alias-brand-primary);
|
|
3615
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary) 22%, transparent);
|
|
3616
|
-
}
|
|
3617
|
-
|
|
3618
|
-
.dshr-compSelect:disabled {
|
|
3619
|
-
border-color: var(--dsw-alias-border-l1);
|
|
3620
|
-
background-color: var(--dsw-alias-bg-layer-1);
|
|
3621
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3622
|
-
cursor: not-allowed;
|
|
3623
|
-
}
|
|
3624
|
-
|
|
3625
|
-
/* 动作行:把按钮与它旁边的提示排在一条基线上。两个动作行(访问测试 / 契约体检)
|
|
3626
|
-
共用 `dshr-compBtnRow` —— 那条抬特异性的字色规则就挂在它上面(见下)。 */
|
|
3627
|
-
.dshr-compActions {
|
|
3628
|
-
display: flex;
|
|
3629
|
-
align-items: center;
|
|
3630
|
-
gap: 12px;
|
|
3631
|
-
}
|
|
3632
|
-
|
|
3633
|
-
.dshr-compHint {
|
|
3634
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3635
|
-
font-size: 12px;
|
|
3636
|
-
line-height: 18px;
|
|
3637
|
-
}
|
|
3638
|
-
|
|
3639
|
-
/* 结果区:成功/失败/请求失败 三种语气。底下垫一层极淡的状态色,比只有一行
|
|
3640
|
-
彩色文字更容易扫到 —— 自检的结果是这一页唯一重要的信息。 */
|
|
3641
|
-
.dshr-compNotice {
|
|
3642
|
-
display: flex;
|
|
3643
|
-
gap: 10px;
|
|
3644
|
-
padding: 12px 14px;
|
|
3645
|
-
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
3646
|
-
border-radius: 12px;
|
|
3647
|
-
background: var(--dsw-alias-bg-layer-1);
|
|
3648
|
-
}
|
|
3649
|
-
|
|
3650
|
-
.dshr-compNotice[data-tone="ok"] {
|
|
3651
|
-
border-color: color-mix(in srgb, var(--dsw-alias-state-success-primary) 34%, transparent);
|
|
3652
|
-
background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 8%, transparent);
|
|
3653
|
-
}
|
|
3654
|
-
|
|
3655
|
-
.dshr-compNotice[data-tone="fail"],
|
|
3656
|
-
.dshr-compNotice[data-tone="network"] {
|
|
3657
|
-
border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 34%, transparent);
|
|
3658
|
-
background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);
|
|
3659
|
-
}
|
|
3660
|
-
|
|
3661
|
-
.dshr-compNoticeIcon {
|
|
3662
|
-
flex: none;
|
|
3663
|
-
width: 16px;
|
|
3664
|
-
height: 16px;
|
|
3665
|
-
margin-top: 2px;
|
|
3666
|
-
color: var(--dsw-alias-state-success-secondary);
|
|
3667
|
-
}
|
|
3668
|
-
|
|
3669
|
-
.dshr-compNotice[data-tone="fail"] .dshr-compNoticeIcon,
|
|
3670
|
-
.dshr-compNotice[data-tone="network"] .dshr-compNoticeIcon {
|
|
3671
|
-
color: var(--dsw-alias-state-error-primary);
|
|
3672
|
-
}
|
|
3673
|
-
|
|
3674
|
-
.dshr-compNoticeBody {
|
|
3675
|
-
display: flex;
|
|
3676
|
-
flex-direction: column;
|
|
3677
|
-
gap: 2px;
|
|
3678
|
-
min-width: 0;
|
|
3679
|
-
}
|
|
3680
|
-
|
|
3681
|
-
.dshr-compNoticeTitle {
|
|
3682
|
-
color: var(--dsw-alias-label-primary);
|
|
3683
|
-
font-size: 13px;
|
|
3684
|
-
font-weight: 500;
|
|
3685
|
-
line-height: 20px;
|
|
3686
|
-
}
|
|
3687
|
-
|
|
3688
|
-
.dshr-compNoticeText {
|
|
3689
|
-
color: var(--dsw-alias-label-secondary);
|
|
3690
|
-
font-size: 12.5px;
|
|
3691
|
-
line-height: 19px;
|
|
3692
|
-
/* 上游可能回一整段 HTML/JSON,限定宽度换行,别把布局撑破 */
|
|
3693
|
-
overflow-wrap: anywhere;
|
|
3694
|
-
}
|
|
3695
|
-
|
|
3696
|
-
/* 回显测的是哪三样。一条「通过」如果不带对象,就是没法归因的一串绿字。 */
|
|
3697
|
-
.dshr-compNoticeMeta {
|
|
3698
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3699
|
-
font-size: 12px;
|
|
3700
|
-
line-height: 18px;
|
|
3701
|
-
overflow-wrap: anywhere;
|
|
3702
|
-
}
|
|
3703
|
-
|
|
3704
|
-
/* 宿主 Button 原语的 primary 想把字色设成 --dsw-alias-label-primary-foreground,
|
|
3705
|
-
但上面那条兜底 `.dshr-settings button { color: inherit; font: inherit }`
|
|
3706
|
-
特异性 (1,1) **高于**原语的 `.primary` (1,0) —— 赢的是它。于是按钮变成
|
|
3707
|
-
「#0f1115 的底 + 继承来的 #0f1115 的字」,黑底黑字、按钮看着是空的。
|
|
3708
|
-
|
|
3709
|
-
按本文件既有的抬特异性办法修(见文件里 .dshr-tab-on / .dshr-period 那几处注释:
|
|
3710
|
-
凡是受这条兜底影响的规则,都必须带挂载容器抬到 (2,0) 以上)。这里
|
|
3711
|
-
`.dshr-comp .dshr-compBtnRow button` 是 (2,1),不依赖源码顺序,以后有人挪块也不会翻车。
|
|
3712
|
-
|
|
3713
|
-
作用域挂在**动作行的共用类 `dshr-compBtnRow`** 上,不是某个具体容器:上一版这条
|
|
3714
|
-
只写了 `.dshr-compActions`,随后新增的「契约体检」按钮在另一个容器里,于是又变回
|
|
3715
|
-
黑底黑字。现在两个动作行共用这个类,再加按钮只需沿用它。
|
|
3716
|
-
|
|
3717
|
-
修的是**把原语的意图还回去**,不是自己另挑一个颜色:字色仍由原语那个 token 决定。 */
|
|
3718
|
-
.dshr-comp .dshr-compBtnRow button {
|
|
3719
|
-
color: var(--dsw-alias-label-primary-foreground);
|
|
3720
|
-
font-size: 14px;
|
|
3721
|
-
line-height: 22px;
|
|
3722
|
-
}
|
|
3723
|
-
|
|
3724
|
-
/* --- 契约体检区(插件自检面板的第二块) ---
|
|
3725
|
-
状态靠 data-state 上色,和「结果卡」那套语气一致:过了是绿、失败是红、
|
|
3726
|
-
没实现是灰、不执行是中性(它是**故意**不跑的,不是问题)。 */
|
|
3727
|
-
|
|
3728
|
-
.dshr-compSection {
|
|
3729
|
-
display: flex;
|
|
3730
|
-
flex-direction: column;
|
|
3731
|
-
gap: 12px;
|
|
3732
|
-
margin-top: 4px;
|
|
3733
|
-
padding-top: 16px;
|
|
3734
|
-
border-top: 0.5px solid var(--dsw-alias-border-l1);
|
|
3735
|
-
}
|
|
3736
|
-
|
|
3737
|
-
.dshr-compHead {
|
|
3738
|
-
display: flex;
|
|
3739
|
-
align-items: baseline;
|
|
3740
|
-
justify-content: space-between;
|
|
3741
|
-
gap: 8px;
|
|
3742
|
-
}
|
|
3743
|
-
|
|
3744
|
-
.dshr-compSectionTitle {
|
|
3745
|
-
margin: 0;
|
|
3746
|
-
color: var(--dsw-alias-label-primary);
|
|
3747
|
-
font-size: 14px;
|
|
3748
|
-
font-weight: 500;
|
|
3749
|
-
line-height: 20px;
|
|
3750
|
-
}
|
|
3751
|
-
|
|
3752
|
-
.dshr-compSectionNote {
|
|
3753
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3754
|
-
font-size: 12px;
|
|
3755
|
-
line-height: 18px;
|
|
3756
|
-
}
|
|
3757
|
-
|
|
3758
|
-
.dshr-compProbeRow {
|
|
3759
|
-
display: flex;
|
|
3760
|
-
align-items: flex-end;
|
|
3761
|
-
gap: 12px;
|
|
3762
|
-
}
|
|
3763
|
-
|
|
3764
|
-
.dshr-compProbeRow .dshr-compField {
|
|
3765
|
-
flex: 1;
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
.dshr-compProbeBtn {
|
|
3769
|
-
flex: none;
|
|
3770
|
-
}
|
|
3771
|
-
|
|
3772
|
-
.dshr-compReport {
|
|
3773
|
-
display: flex;
|
|
3774
|
-
flex-direction: column;
|
|
3775
|
-
gap: 8px;
|
|
3776
|
-
padding: 12px 14px;
|
|
3777
|
-
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
3778
|
-
border-radius: 12px;
|
|
3779
|
-
background: var(--dsw-alias-bg-layer-1);
|
|
3780
|
-
}
|
|
3781
|
-
|
|
3782
|
-
.dshr-compReportHead {
|
|
3783
|
-
display: flex;
|
|
3784
|
-
align-items: baseline;
|
|
3785
|
-
justify-content: space-between;
|
|
3786
|
-
gap: 10px;
|
|
3787
|
-
flex-wrap: wrap;
|
|
3788
|
-
}
|
|
3789
|
-
|
|
3790
|
-
.dshr-compReportName {
|
|
3791
|
-
color: var(--dsw-alias-label-primary);
|
|
3792
|
-
font-size: 13px;
|
|
3793
|
-
font-weight: 500;
|
|
3794
|
-
line-height: 20px;
|
|
3795
|
-
}
|
|
3796
|
-
|
|
3797
|
-
.dshr-compReportSum {
|
|
3798
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3799
|
-
font-size: 12px;
|
|
3800
|
-
line-height: 18px;
|
|
3801
|
-
}
|
|
3802
|
-
|
|
3803
|
-
.dshr-compMembers {
|
|
3804
|
-
display: flex;
|
|
3805
|
-
flex-direction: column;
|
|
3806
|
-
margin: 0;
|
|
3807
|
-
padding: 0;
|
|
3808
|
-
list-style: none;
|
|
3809
|
-
}
|
|
3810
|
-
|
|
3811
|
-
.dshr-compMember {
|
|
3812
|
-
display: grid;
|
|
3813
|
-
grid-template-columns: 16px minmax(0, auto) minmax(0, 1fr);
|
|
3814
|
-
align-items: baseline;
|
|
3815
|
-
gap: 10px;
|
|
3816
|
-
padding: 5px 0;
|
|
3817
|
-
border-bottom: 0.5px solid var(--dsw-alias-border-l1);
|
|
3818
|
-
}
|
|
3819
|
-
|
|
3820
|
-
.dshr-compMember:last-child {
|
|
3821
|
-
border-bottom: 0;
|
|
3822
|
-
}
|
|
3823
|
-
|
|
3824
|
-
.dshr-compMemberState {
|
|
3825
|
-
flex: none;
|
|
3826
|
-
text-align: center;
|
|
3827
|
-
font-size: 12px;
|
|
3828
|
-
line-height: 18px;
|
|
3829
|
-
}
|
|
3830
|
-
|
|
3831
|
-
.dshr-compMember[data-state="ok"] .dshr-compMemberState {
|
|
3832
|
-
color: var(--dsw-alias-state-success-secondary);
|
|
3833
|
-
}
|
|
3834
|
-
|
|
3835
|
-
.dshr-compMember[data-state="fail"] .dshr-compMemberState {
|
|
3836
|
-
color: var(--dsw-alias-state-error-primary);
|
|
3837
|
-
}
|
|
3838
|
-
|
|
3839
|
-
.dshr-compMember[data-state="absent"] .dshr-compMemberState,
|
|
3840
|
-
.dshr-compMember[data-state="skipped"] .dshr-compMemberState {
|
|
3841
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3842
|
-
}
|
|
3843
|
-
|
|
3844
|
-
.dshr-compMemberName {
|
|
3845
|
-
color: var(--dsw-alias-label-primary);
|
|
3846
|
-
font-size: 12.5px;
|
|
3847
|
-
line-height: 19px;
|
|
3848
|
-
white-space: nowrap;
|
|
3849
|
-
}
|
|
3850
|
-
|
|
3851
|
-
.dshr-compMemberKey {
|
|
3852
|
-
margin-left: 6px;
|
|
3853
|
-
color: var(--dsw-alias-label-tertiary);
|
|
3854
|
-
font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
3855
|
-
font-size: 11px;
|
|
3856
|
-
}
|
|
3857
|
-
|
|
3858
|
-
.dshr-compMemberDetail {
|
|
3859
|
-
color: var(--dsw-alias-label-secondary);
|
|
3860
|
-
font-size: 12px;
|
|
3861
|
-
line-height: 18px;
|
|
3862
|
-
overflow-wrap: anywhere;
|
|
3863
|
-
}
|
|
3864
|
-
|
|
3865
|
-
|
package/src/ext/contract.ts
CHANGED
|
@@ -182,7 +182,7 @@ export interface ExtControl {
|
|
|
182
182
|
/**
|
|
183
183
|
* 分组名(可选;缺省 = 无组)—— 面板按它把控制条**分节**渲染,组间插一条
|
|
184
184
|
* 组标题行,组内顺序照旧。**无组的条目照旧平铺** ⇒ 没用这个字段的扩展
|
|
185
|
-
*
|
|
185
|
+
* 面板行为一个像素都不变。
|
|
186
186
|
*
|
|
187
187
|
* 为什么要有:一条扩展同时管两类东西时(分层提示词 = 准则分类 + 宿主段落),
|
|
188
188
|
* 平铺成一长列会让"哪条是准则、哪条是宿主的段"无从分辨 —— 分组是**归属
|
|
@@ -26,6 +26,17 @@
|
|
|
26
26
|
* `categories`/`text`/`order` 里残留的 `hardening` 键无须迁移 ——
|
|
27
27
|
* `resolveCategories` 规则 2 会忽略不认识的 id。
|
|
28
28
|
* ② 身份气质:「默认中文」→「回复、思考过程必须使用中文」。
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ 2026-10-27 用户点名的四处改动:
|
|
31
|
+
* ① **删掉「可判定」分类**(整条移除,id `verdifiable`);已存配置里的残留键
|
|
32
|
+
* 由 `resolveCategories` 忽略,无须迁移。
|
|
33
|
+
* ② **删掉「绝不偷懒」分类**(整条移除,id `no-cheap`)——用户判定它与别处重复。
|
|
34
|
+
* ③ 执行顺序重写:去掉「查结构行号 / 声明任务四件套 / 读码取切片 / 循既有骨架 /
|
|
35
|
+
* 以闸门为准」几段,改为「循既有架构/提出更优架构 → 验证以测试/e2e 验收」;
|
|
36
|
+
* 并在首尾补上「读文档」与「更新文档」—— 任务不是从零开始,也不以提交收尾。
|
|
37
|
+
* ④ **新增「文档」分类**(id `doc`):用户口述「文档不写流水账、只写权威内容」
|
|
38
|
+
* + 点名范围「docs/ 下的调研、设计、任务」,本文件只做润色。
|
|
39
|
+
* 排在「全局收口」之后 —— 收口查"文档过期没有",这条管"文档该写什么"。
|
|
29
40
|
*/
|
|
30
41
|
|
|
31
42
|
/** 一个可独立开关的提示词分类。 */
|
|
@@ -87,11 +98,12 @@ export const PROMPT_CATEGORIES: readonly PromptCategory[] = [
|
|
|
87
98
|
'做完那块区域该比动手前更整或更少。',
|
|
88
99
|
},
|
|
89
100
|
{
|
|
90
|
-
id: '
|
|
91
|
-
title: '
|
|
101
|
+
id: 'doc',
|
|
102
|
+
title: '文档',
|
|
92
103
|
defaultOn: true,
|
|
93
104
|
body:
|
|
94
|
-
'
|
|
105
|
+
'文档:docs/ 里的调研/设计/任务文档,只写权威内容——结论、约束、怎么用;' +
|
|
106
|
+
'不写流水账(过程、试错、时间线),历史归 git。',
|
|
95
107
|
},
|
|
96
108
|
{
|
|
97
109
|
id: 'root-cause',
|
|
@@ -123,13 +135,6 @@ export const PROMPT_CATEGORIES: readonly PromptCategory[] = [
|
|
|
123
135
|
'结构:先让结构对再让代码短;命名/边界/单一职责优先于行数;依赖单向无环、公共逻辑收敛一处;' +
|
|
124
136
|
'单文件单功能,软顶函数 ~40 行/文件 ~300 行。',
|
|
125
137
|
},
|
|
126
|
-
{
|
|
127
|
-
id: 'no-cheap',
|
|
128
|
-
title: '绝不偷懒',
|
|
129
|
-
defaultOn: true,
|
|
130
|
-
body:
|
|
131
|
-
'绝不偷懒:边界输入校验、防数据丢失、安全、可访问性、真实硬件校准;非平凡逻辑留个能跑的检查。',
|
|
132
|
-
},
|
|
133
138
|
{
|
|
134
139
|
id: 'delivery',
|
|
135
140
|
title: '交付',
|
|
@@ -143,8 +148,8 @@ export const PROMPT_CATEGORIES: readonly PromptCategory[] = [
|
|
|
143
148
|
title: '执行顺序',
|
|
144
149
|
defaultOn: true,
|
|
145
150
|
body:
|
|
146
|
-
'
|
|
147
|
-
'
|
|
151
|
+
'执行:拉记忆→读文档→看全貌→定位任务→建领地→循既有架构/提出更优架构→验证以测试/e2e 验收→' +
|
|
152
|
+
'收口复查全貌→更新文档→提交。',
|
|
148
153
|
},
|
|
149
154
|
] as const
|
|
150
155
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 1. 登记进 `router.ext` 表,带上可显示的身份(名字/说明/就绪);
|
|
4
4
|
* 2. **幂等**:同一个 id 已在表里就不顶掉别人的登记;
|
|
5
5
|
* 3. `source: 'builtin'`(随核心分发 ⇒ 插件页的原生行,自绘节不重复列);
|
|
6
|
-
* 4. **准则段落真的挂上了** ——
|
|
6
|
+
* 4. **准则段落真的挂上了** —— 这是本扩展唯一的执行面,必须单独验。
|
|
7
7
|
*
|
|
8
8
|
* 为什么第 4 条不可省:扩展的「卡片出现与否」取决于它在不在这张表里,而**准则
|
|
9
9
|
* 有没有进 prompt** 取决于段落挂没挂 —— 两者是**不同的失效**,都静默且都不报错。
|
|
@@ -75,7 +75,7 @@ test('★ patch 里真的声明了这一行,且 id/name 逐字对得上', () =
|
|
|
75
75
|
assert.equal(block![1], 'dsh-router-core/ext-prompt', 'patch 的 name 必须是能解析的子路径说明符')
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
test('★
|
|
78
|
+
test('★ 那一行默认关闭', () => {
|
|
79
79
|
// `disabled: true` = 行默认关 ⇒ loader 不 import ⇒ 扩展不进 `router.ext` 表 ⇒
|
|
80
80
|
// 卡片不出现、准则不进 prompt。
|
|
81
81
|
//
|
|
@@ -148,7 +148,7 @@ test('幂等:同一个 id 已在表里时不顶掉别人的登记', async () =
|
|
|
148
148
|
assert.equal(table[EXT_PROMPT_ID], first, '重复登记不该换成一个新对象')
|
|
149
149
|
})
|
|
150
150
|
|
|
151
|
-
test('★ apply 把准则挂成了 system prompt
|
|
151
|
+
test('★ apply 把准则挂成了 system prompt 段落', async () => {
|
|
152
152
|
const { ctx, sections } = await realCtx()
|
|
153
153
|
apply(ctx as unknown as Parameters<typeof apply>[0])
|
|
154
154
|
assert.equal(sections.length, 1, '准则没挂上 = 卡片亮着但 prompt 里没有准则')
|
package/src/ext-prompt/index.ts
CHANGED
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* 分层提示词扩展的 **cordis 行** —— 官方插件页(设置 → 插件 → dsh-router-core)详情页
|
|
3
3
|
* 原生「包含的组件」里的那一行。
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 本扩展要把准则真注入,所以额外等 `systemPrompt` service 就绪后挂 section。
|
|
5
|
+
* 壳的形状 = cordis 身份 + 登记,实现 = `plugin.ts`;**执行面的事**是挂
|
|
6
|
+
* system prompt 段落 —— 所以额外等 `systemPrompt` service 就绪后挂 section。
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* 让准则默认缺席,会变成「用户不知道它存在 ⇒ 协作按另一套规则执行」。
|
|
8
|
+
* **默认关闭**(patch 里 `disabled: true`):开不开由用户决定。行关闭时 loader
|
|
9
|
+
* 根本不 import 本模块,扩展就不在 `router.ext` 表里 ——「打开才出现这张卡片」
|
|
10
|
+
* 是天然的,不需要额外状态位。
|
|
13
11
|
*
|
|
14
12
|
* 登记时把扩展开关置为开:本扩展**没有独立的运行期开关**,它的开关就是这一行
|
|
15
13
|
* (宿主管的那个)。不置上的话,用户开了行还要再点一次,两级开关说同一件事。
|
|
@@ -52,8 +50,8 @@ function systemPromptOf(sctx: Context): SystemPromptFace | undefined {
|
|
|
52
50
|
*
|
|
53
51
|
* ⚠️⚠️ **必须导出这个数组,且 `apply` 里直接用 `ctx.systemPrompt`**(2026-09-29 修)
|
|
54
52
|
* ——我原先写成「在 apply 里 `ctx.inject(['systemPrompt'], cb)`」,**准则一直不进
|
|
55
|
-
* prompt**:面板上卡片、开关、原文全都正常(`GET /router/api/ext`
|
|
56
|
-
*
|
|
53
|
+
* prompt**:面板上卡片、开关、原文全都正常(`GET /router/api/ext` 里 controls 一条
|
|
54
|
+
* 不少),但模型看到的 system prompt 里没有它。
|
|
57
55
|
*
|
|
58
56
|
* 根因(本机 cordis 4.0.4 实测):`ctx.inject(deps, cb)` 的回调**不是同步触发** ——
|
|
59
57
|
* provide 之后立刻 `inject`,回调要等到下一个异步点才跑。插件的 `apply` 是**同步
|
package/src/ext-prompt/mount.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* 挂载层 —— 把准则注册成 `ctx.systemPrompt` 的**一个**段落。
|
|
3
3
|
*
|
|
4
4
|
* ════════════════════════════════════════════════════════════════════
|
|
5
|
-
*
|
|
5
|
+
* 为什么只注册一段,而不是一段一个分类
|
|
6
6
|
* ════════════════════════════════════════════════════════════════════
|
|
7
|
-
* 面板开关的粒度是「分类」,但 prompt
|
|
7
|
+
* 面板开关的粒度是「分类」,但 prompt 读起来应该是一段连续的准则。分成多段
|
|
8
8
|
* 会让 KV cache 每个分类各占一段,且**增删任意一个分类都让它之后的全部分位**。
|
|
9
9
|
* 一段 + 拼装 ⇒ 分类增删只改这一段内部,段的位置与前缀稳定。
|
|
10
10
|
*
|
package/src/ext-prompt/plugin.ts
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 分层提示词扩展(`router.ext` 侧的实现)—— 声明与状态都在这里。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 注册成一段 system prompt。
|
|
4
|
+
* **不挂任何监听、不改写任何东西**,唯一作用是让用户在官方插件页里看到一张
|
|
5
|
+
* 卡片、能开关它,并把协作准则注册成一段 system prompt。
|
|
7
6
|
*
|
|
8
7
|
* 与 `rtk` / `jev` 那类扩展的区别要写清楚:那两家挂 `tools/execute`
|
|
9
8
|
* 改写**工具调用**;本扩展只往 **system prompt** 加一段。名字同族是因为
|
|
10
9
|
* 都在 `router.ext` 这张表里,拦截面不重叠。
|
|
11
10
|
*
|
|
12
|
-
* 复用 `router.ext` 通道只为拿到**发现 +
|
|
13
|
-
* 真正的注入在 `mount.ts`。
|
|
11
|
+
* 复用 `router.ext` 通道只为拿到**发现 + 开关**两件事,真正的注入在 `mount.ts`。
|
|
14
12
|
*/
|
|
15
13
|
import type { ExtStoreService, RouterExt } from '../ext/contract.ts'
|
|
16
14
|
import { PROMPT_CATEGORIES, PROMPT_TITLE } from './content.ts'
|
package/src/router/index.ts
CHANGED
|
@@ -1043,9 +1043,8 @@ export class Router {
|
|
|
1043
1043
|
// 注意这里**刻意不**把「用户禁用过的模型」也补回来:插件的 listModels 只该报
|
|
1044
1044
|
// 「模型来源」,启用状态由核心合并(见 loader.ts 的 modelsWithEnabled)。
|
|
1045
1045
|
// 插件若在 listModels 里就把自己禁用的过滤掉,那是它违约 —— 该修的是插件,
|
|
1046
|
-
//
|
|
1047
|
-
//
|
|
1048
|
-
// 永远报 OK,那个 bug 就再也没人发现了。
|
|
1046
|
+
// 核心不该反过来替它兜着:兜着之后面板看着正常了,插件那个 bug 就再也没人
|
|
1047
|
+
// 发现了(症状消失、原因还在)。少报就让它少报,用户看得见才好去修插件。
|
|
1049
1048
|
const seen = new Set(models.map((mm) => mm.id))
|
|
1050
1049
|
for (const id of custom) {
|
|
1051
1050
|
if (!seen.has(id)) {
|