dsh-peak-block 0.1.0 → 0.2.0
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 +26 -8
- package/cordis.patch.yml +1 -1
- package/lib/index.d.ts +93 -0
- package/lib/index.js +87 -82
- package/package.json +24 -13
- package/lib/client.js +0 -191
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# dsh
|
|
1
|
+
# dsh·禁止梁文峰
|
|
2
2
|
|
|
3
3
|
在 DeepSeek 官方高峰时段拦截官方 provider 请求,保障安心使用梁文谷。
|
|
4
4
|
|
|
@@ -10,13 +10,27 @@
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
## 演示视频
|
|
14
|
+
|
|
15
|
+
| 禁止梁文峰演示 · 24 秒 |
|
|
16
|
+
| :---: |
|
|
17
|
+
| [](https://www.bilibili.com/video/BV1zvth6oEFp/) |
|
|
18
|
+
|
|
13
19
|
## 安装
|
|
14
20
|
|
|
21
|
+
**从 GitHub 安装**:源码在 `src/`,`lib/` 不入仓库,安装时 npm 会触发 `prepare` 脚本现场构建。
|
|
22
|
+
|
|
15
23
|
```powershell
|
|
16
24
|
dsh plugin --profile web add github:better-er/dsh-peak-block
|
|
17
25
|
```
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
**从 npm 安装**:包内已含构建产物 `lib/index.js`,安装时不再构建。
|
|
28
|
+
|
|
29
|
+
```powershell
|
|
30
|
+
dsh plugin --profile web add dsh-peak-block
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
两种方式装完都会自动挂载,重启 DSH web 后启用,无需手工编辑任何文件。
|
|
20
34
|
|
|
21
35
|
## 卸载
|
|
22
36
|
|
|
@@ -33,7 +47,7 @@ dsh plugin --profile web remove dsh-peak-block
|
|
|
33
47
|
| 键 | 默认 | 说明 |
|
|
34
48
|
|---|---|---|
|
|
35
49
|
| `enabled` | `true` | 总开关 |
|
|
36
|
-
| `officialProviders` |
|
|
50
|
+
| `officialProviders` | 未设,只认 `deepseek-official` | 视为「官方」的 provider 精确名单,不设用默认判定 |
|
|
37
51
|
| `targetProvider` | 空 | 拦截后切到的目标 provider;留空 = 阻止并提示 |
|
|
38
52
|
| `peakWindow` | 工作日 9–12、14–18,周末谷 | 峰谷时段窗口,可整体或局部覆盖 |
|
|
39
53
|
|
|
@@ -52,20 +66,24 @@ plugins:
|
|
|
52
66
|
|
|
53
67
|
## 边界
|
|
54
68
|
|
|
55
|
-
- 只拦对话模型请求:`agent/request` 覆盖 agent loop 的正常对话;compaction 等走 `ctx.llm.stream
|
|
69
|
+
- 只拦对话模型请求:`agent/request` 覆盖 agent loop 的正常对话;compaction 等走 `ctx.llm.stream` 的路径不拦。
|
|
56
70
|
- `targetProvider` 必须是 DSH 已注册适配器路由,否则切换后以 `NO_ADAPTER` 失败。
|
|
57
71
|
- `days` 用 JS `getUTCDay` 序号,1..5 为周一..周五;时区判定纯 UTC+8 数学换算,与系统时区无关。
|
|
58
72
|
|
|
59
73
|
## 要求与开发
|
|
60
74
|
|
|
61
|
-
- 是**标准形态的 dsh
|
|
62
|
-
-
|
|
63
|
-
- 纯函数 `isPeakBeijing` / `isOfficial` / `decide` 在 `lib/index.js` 导出,冒烟测试:
|
|
75
|
+
- 是**标准形态的 dsh host 单半身插件**:host 在 `agent/request` waterfall 里拦截并切换/阻止,设置全部经 cordis 配置文件注入,见上方配置表,不提供界面配置 UI。
|
|
76
|
+
- TypeScript 源码在 `src/index.ts`,由 tsdown 构建到 `lib/`;`lib/` 是产物不入库,改完要重新构建:
|
|
64
77
|
|
|
65
78
|
```powershell
|
|
66
|
-
|
|
79
|
+
pnpm install
|
|
80
|
+
pnpm build # 构建 lib/index.js 与 lib/index.d.ts
|
|
81
|
+
pnpm typecheck # 严格类型检查
|
|
82
|
+
pnpm test # vitest 单测,覆盖峰谷判定与拦截决策
|
|
67
83
|
```
|
|
68
84
|
|
|
85
|
+
- 纯函数 `isPeakBeijing` / `isOfficial` / `decide` 从包入口导出,单测见 `tests/index.spec.ts`。
|
|
86
|
+
|
|
69
87
|
## License
|
|
70
88
|
|
|
71
89
|
[MIT](./LICENSE)
|
package/cordis.patch.yml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# dsh-peak-block bundle patch: inserts this plugin into a profile's layer stack.
|
|
2
|
-
# Applied when a profile lists this bundle
|
|
2
|
+
# Applied when a profile lists this bundle or wired by dev_install_package.
|
|
3
3
|
# targetProvider 留空即「未配置拦截目标」,梁文峰时间拦截到时走「阻止并提示」。
|
|
4
4
|
- insert:
|
|
5
5
|
- id: dsh-peak-block
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
//#region src/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* dsh-peak-block host 端:梁文峰时间拦截向 DeepSeek 官方 API 的模型请求。
|
|
4
|
+
*
|
|
5
|
+
* 在「梁文峰时间」,即 DeepSeek 官方高峰时段——北京时间工作日 09:00–12:00、14:00–18:00、周末全天谷价——拦截原本发往官方 provider 的对话请求。
|
|
6
|
+
* 已配置 targetProvider 则切换路由,未配置则阻止并抛出带文案的错误提示。非高峰时段不拦截,正常走官方。
|
|
7
|
+
*
|
|
8
|
+
* 拦截 seam:agent/request waterfall。它对每次对话模型请求携带冻结的调用配置种子 LlmCallConfig,含 provider/model/reasoningEffort/temperature/maxTokens/stop。
|
|
9
|
+
* listener 可返回替代 config 切换 provider。要判断请求是否官方必须先调用 next 拿到 config,故实现为「先取 config 再决策」:切换时返回替代 config,阻止时抛带提示文案的错误。
|
|
10
|
+
* compaction 等走 ctx.llm.stream 直接调用的路径不经此 waterfall,本插件不拦。
|
|
11
|
+
*/
|
|
12
|
+
/** 插件名,与 cordis.patch.yml 的 name 一致。 */
|
|
13
|
+
declare const name = "dsh-peak-block";
|
|
14
|
+
/** 纯 host 半身,无额外服务注入。 */
|
|
15
|
+
declare const inject: string[];
|
|
16
|
+
/** 峰谷时段窗口。 */
|
|
17
|
+
interface PeakWindow {
|
|
18
|
+
/** JS getUTCDay 序号:0=周日 … 6=周六,1..5 为周一..周五。 */
|
|
19
|
+
days: number[];
|
|
20
|
+
/** 峰时段列表,每项 [起, 止),小时制,含起不含止,按 UTC+8 换算。 */
|
|
21
|
+
hourRanges: Array<[number, number]>;
|
|
22
|
+
/** 周末是否全天谷价,默认 true,即周末不拦。 */
|
|
23
|
+
weekendOffPeak: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** 梁文峰时间默认窗口:工作日 09:00–12:00、14:00–18:00,UTC+8,周末全天谷。 */
|
|
26
|
+
declare const DEFAULT_PEAK: PeakWindow;
|
|
27
|
+
/** 模型请求的冻结调用配置种子,agent/request waterfall 的 next() 返回它,切换 provider 时原样带出其余字段。 */
|
|
28
|
+
interface CallSeed {
|
|
29
|
+
provider?: string;
|
|
30
|
+
model?: string;
|
|
31
|
+
reasoningEffort?: string;
|
|
32
|
+
temperature?: number;
|
|
33
|
+
maxTokens?: number;
|
|
34
|
+
stop?: string[];
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** 插件配置,全部经 cordis 配置文件注入。 */
|
|
38
|
+
interface Config {
|
|
39
|
+
/** 总开关,显式 false 才关闭,缺省启用。 */
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
/** 视为「官方」的 provider 精确名单,不设则用默认判定,即只认 deepseek-official。 */
|
|
42
|
+
officialProviders?: string[];
|
|
43
|
+
/** 拦截后切到的目标 provider,留空即阻止并提示。 */
|
|
44
|
+
targetProvider?: string;
|
|
45
|
+
/** 峰谷时段窗口,可整体或局部覆盖默认窗口。 */
|
|
46
|
+
peakWindow?: Partial<PeakWindow>;
|
|
47
|
+
}
|
|
48
|
+
/** 单次请求的决策结果:pass 放行、switch 切 provider、block 阻止。 */
|
|
49
|
+
type Decision = {
|
|
50
|
+
action: 'pass';
|
|
51
|
+
} | {
|
|
52
|
+
action: 'switch';
|
|
53
|
+
config: CallSeed;
|
|
54
|
+
} | {
|
|
55
|
+
action: 'block';
|
|
56
|
+
};
|
|
57
|
+
/** 决策输入,timeMs 由调用方注入以便测试。 */
|
|
58
|
+
interface DecideOptions {
|
|
59
|
+
/** 判定用的 epoch 毫秒,本地时钟不可信时也照此换算 UTC+8。 */
|
|
60
|
+
timeMs: number;
|
|
61
|
+
/** 峰谷窗口,缺省用 DEFAULT_PEAK。 */
|
|
62
|
+
peakWindow?: Partial<PeakWindow>;
|
|
63
|
+
/** 官方 provider 精确名单,缺省只认 deepseek-official。 */
|
|
64
|
+
officialProviders?: string[];
|
|
65
|
+
/** 拦截后切到的目标 provider。 */
|
|
66
|
+
targetProvider?: string;
|
|
67
|
+
}
|
|
68
|
+
/** host 上下文里本插件用到的最小接口,只需注册 agent/request waterfall。 */
|
|
69
|
+
interface HostContext {
|
|
70
|
+
on(event: 'agent/request', listener: (payload: unknown, next: () => Promise<CallSeed>) => Promise<CallSeed>): void;
|
|
71
|
+
}
|
|
72
|
+
/** 默认官方 provider 判定:只精确匹配 DSH 官方注册路由 deepseek-official。
|
|
73
|
+
* 不用名称前缀规则——pi-ai 自带的第三方 deepseek 中转不应算官方,避免误拦。 */
|
|
74
|
+
declare function defaultIsOfficial(provider: string | undefined): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* 时刻是否处于梁文峰时间。纯 UTC+8 数学换算,与系统时区无关,本机时钟/时区不可信。
|
|
77
|
+
* 红线:周末全天谷价,仅工作日有峰。
|
|
78
|
+
*/
|
|
79
|
+
declare function isPeakBeijing(timeMs: number, peak?: Partial<PeakWindow>): boolean;
|
|
80
|
+
/** provider 是否视为官方。显式名单用精确匹配;未提供名单用默认判定,即仅 deepseek-official。 */
|
|
81
|
+
declare function isOfficial(provider: string | undefined, officialProviders?: string[]): boolean;
|
|
82
|
+
/** 阻止时抛出的提示文案,用户可见,随界面失败信息呈现。 */
|
|
83
|
+
declare const BLOCK_MESSAGE = "【梁文峰时间拦截 · dsh-peak-block】当前为 DeepSeek 官方高峰时段,且未配置拦截目标 targetProvider,请求已阻止。请为 dsh-peak-block 配置 targetProvider,或将请求留到梁文谷时段再发。";
|
|
84
|
+
/**
|
|
85
|
+
* 纯拦截决策,可单测:返回 { action: 'pass' | 'switch' | 'block', config? }。
|
|
86
|
+
* - 非梁文峰时间或非官方 → pass,放行
|
|
87
|
+
* - 梁文峰时间 + 官方 + 有目标 → switch,切 provider 到目标
|
|
88
|
+
* - 梁文峰时间 + 官方 + 无目标 → block,阻止并提示
|
|
89
|
+
*/
|
|
90
|
+
declare function decide(seed: CallSeed | undefined, opts: DecideOptions): Decision;
|
|
91
|
+
declare function apply(ctx: HostContext, config?: Config): void;
|
|
92
|
+
//#endregion
|
|
93
|
+
export { BLOCK_MESSAGE, CallSeed, Config, DEFAULT_PEAK, DecideOptions, Decision, HostContext, PeakWindow, apply, decide, defaultIsOfficial, inject, isOfficial, isPeakBeijing, name };
|
package/lib/index.js
CHANGED
|
@@ -1,88 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
//#region src/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* dsh-peak-block host 端:梁文峰时间拦截向 DeepSeek 官方 API 的模型请求。
|
|
4
|
+
*
|
|
5
|
+
* 在「梁文峰时间」,即 DeepSeek 官方高峰时段——北京时间工作日 09:00–12:00、14:00–18:00、周末全天谷价——拦截原本发往官方 provider 的对话请求。
|
|
6
|
+
* 已配置 targetProvider 则切换路由,未配置则阻止并抛出带文案的错误提示。非高峰时段不拦截,正常走官方。
|
|
7
|
+
*
|
|
8
|
+
* 拦截 seam:agent/request waterfall。它对每次对话模型请求携带冻结的调用配置种子 LlmCallConfig,含 provider/model/reasoningEffort/temperature/maxTokens/stop。
|
|
9
|
+
* listener 可返回替代 config 切换 provider。要判断请求是否官方必须先调用 next 拿到 config,故实现为「先取 config 再决策」:切换时返回替代 config,阻止时抛带提示文案的错误。
|
|
10
|
+
* compaction 等走 ctx.llm.stream 直接调用的路径不经此 waterfall,本插件不拦。
|
|
11
|
+
*/
|
|
12
|
+
/** 插件名,与 cordis.patch.yml 的 name 一致。 */
|
|
13
|
+
const name = "dsh-peak-block";
|
|
14
|
+
/** 纯 host 半身,无额外服务注入。 */
|
|
15
|
+
const inject = [];
|
|
16
|
+
/** 梁文峰时间默认窗口:工作日 09:00–12:00、14:00–18:00,UTC+8,周末全天谷。 */
|
|
17
|
+
const DEFAULT_PEAK = {
|
|
18
|
+
days: [
|
|
19
|
+
1,
|
|
20
|
+
2,
|
|
21
|
+
3,
|
|
22
|
+
4,
|
|
23
|
+
5
|
|
24
|
+
],
|
|
25
|
+
hourRanges: [[9, 12], [14, 18]],
|
|
26
|
+
weekendOffPeak: true
|
|
27
|
+
};
|
|
28
|
+
/** 默认官方 provider 判定:只精确匹配 DSH 官方注册路由 deepseek-official。
|
|
29
|
+
* 不用名称前缀规则——pi-ai 自带的第三方 deepseek 中转不应算官方,避免误拦。 */
|
|
30
|
+
function defaultIsOfficial(provider) {
|
|
31
|
+
return provider === "deepseek-official";
|
|
24
32
|
}
|
|
25
|
-
|
|
26
33
|
/**
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return false
|
|
34
|
+
* 时刻是否处于梁文峰时间。纯 UTC+8 数学换算,与系统时区无关,本机时钟/时区不可信。
|
|
35
|
+
* 红线:周末全天谷价,仅工作日有峰。
|
|
36
|
+
*/
|
|
37
|
+
function isPeakBeijing(timeMs, peak = DEFAULT_PEAK) {
|
|
38
|
+
const shifted = timeMs + 288e5;
|
|
39
|
+
const d = new Date(shifted);
|
|
40
|
+
const day = d.getUTCDay();
|
|
41
|
+
if (peak.weekendOffPeak !== false && (day === 0 || day === 6)) return false;
|
|
42
|
+
const days = peak.days;
|
|
43
|
+
if (Array.isArray(days) && days.length > 0 && !days.includes(day)) return false;
|
|
44
|
+
const hour = d.getUTCHours();
|
|
45
|
+
for (const [a, b] of peak.hourRanges ?? DEFAULT_PEAK.hourRanges) if (hour >= a && hour < b) return true;
|
|
46
|
+
return false;
|
|
41
47
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return officialProviders.some((p) => provider === p)
|
|
48
|
-
}
|
|
49
|
-
return defaultIsOfficial(provider)
|
|
48
|
+
/** provider 是否视为官方。显式名单用精确匹配;未提供名单用默认判定,即仅 deepseek-official。 */
|
|
49
|
+
function isOfficial(provider, officialProviders) {
|
|
50
|
+
if (!provider) return false;
|
|
51
|
+
if (Array.isArray(officialProviders)) return officialProviders.some((p) => provider === p);
|
|
52
|
+
return defaultIsOfficial(provider);
|
|
50
53
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export const BLOCK_MESSAGE =
|
|
54
|
-
'【梁文峰时间拦截 · dsh-peak-block】当前为 DeepSeek 官方高峰时段,且未配置拦截目标(targetProvider),请求已阻止。请为 dsh-peak-block 配置 targetProvider,或将请求留到梁文谷时段再发。'
|
|
55
|
-
|
|
54
|
+
/** 阻止时抛出的提示文案,用户可见,随界面失败信息呈现。 */
|
|
55
|
+
const BLOCK_MESSAGE = "【梁文峰时间拦截 · dsh-peak-block】当前为 DeepSeek 官方高峰时段,且未配置拦截目标 targetProvider,请求已阻止。请为 dsh-peak-block 配置 targetProvider,或将请求留到梁文谷时段再发。";
|
|
56
56
|
/**
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
57
|
+
* 纯拦截决策,可单测:返回 { action: 'pass' | 'switch' | 'block', config? }。
|
|
58
|
+
* - 非梁文峰时间或非官方 → pass,放行
|
|
59
|
+
* - 梁文峰时间 + 官方 + 有目标 → switch,切 provider 到目标
|
|
60
|
+
* - 梁文峰时间 + 官方 + 无目标 → block,阻止并提示
|
|
61
|
+
*/
|
|
62
|
+
function decide(seed, opts) {
|
|
63
|
+
if (!isPeakBeijing(opts.timeMs, opts.peakWindow)) return { action: "pass" };
|
|
64
|
+
if (!isOfficial(seed?.provider, opts.officialProviders)) return { action: "pass" };
|
|
65
|
+
if (opts.targetProvider) return {
|
|
66
|
+
action: "switch",
|
|
67
|
+
config: {
|
|
68
|
+
...seed ?? {},
|
|
69
|
+
provider: opts.targetProvider
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
return { action: "block" };
|
|
73
|
+
}
|
|
74
|
+
function apply(ctx, config = {}) {
|
|
75
|
+
if (config.enabled === false) return;
|
|
76
|
+
const peakWindow = config.peakWindow;
|
|
77
|
+
const officialProviders = config.officialProviders;
|
|
78
|
+
const targetProvider = config.targetProvider;
|
|
79
|
+
ctx.on("agent/request", async (_payload, next) => {
|
|
80
|
+
const seed = await next();
|
|
81
|
+
const decision = decide(seed, {
|
|
82
|
+
timeMs: Date.now(),
|
|
83
|
+
peakWindow,
|
|
84
|
+
officialProviders,
|
|
85
|
+
targetProvider
|
|
86
|
+
});
|
|
87
|
+
if (decision.action === "pass") return seed;
|
|
88
|
+
if (decision.action === "switch") return decision.config;
|
|
89
|
+
throw new Error(BLOCK_MESSAGE);
|
|
90
|
+
});
|
|
69
91
|
}
|
|
70
|
-
|
|
71
|
-
export
|
|
72
|
-
if (config.enabled === false) return
|
|
73
|
-
const peakWindow = config.peakWindow || DEFAULT_PEAK
|
|
74
|
-
const officialProviders = config.officialProviders
|
|
75
|
-
const targetProvider = config.targetProvider
|
|
76
|
-
ctx.on('agent/request', async (_payload, next) => {
|
|
77
|
-
const seed = await next()
|
|
78
|
-
const decision = decide(seed, {
|
|
79
|
-
timeMs: Date.now(),
|
|
80
|
-
peakWindow,
|
|
81
|
-
officialProviders,
|
|
82
|
-
targetProvider,
|
|
83
|
-
})
|
|
84
|
-
if (decision.action === 'pass') return seed
|
|
85
|
-
if (decision.action === 'switch') return decision.config
|
|
86
|
-
throw new Error(BLOCK_MESSAGE)
|
|
87
|
-
})
|
|
88
|
-
}
|
|
92
|
+
//#endregion
|
|
93
|
+
export { BLOCK_MESSAGE, DEFAULT_PEAK, apply, decide, defaultIsOfficial, inject, isOfficial, isPeakBeijing, name };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-peak-block",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "梁文峰时间拦截官方 DeepSeek API 请求:北京时间工作日高峰时段 09:00-12:00、14:00-18:00 自动拦截发往官方 provider `deepseek-official` 的模型请求,可切换到第三方中转或预留的本机转 API 端口,未配置则阻止并提示;非高峰不拦、正常走官方,专治高峰掉速与刷屏。标准可安装 dsh 插件,host
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "梁文峰时间拦截官方 DeepSeek API 请求:北京时间工作日高峰时段 09:00-12:00、14:00-18:00 自动拦截发往官方 provider `deepseek-official` 的模型请求,可切换到第三方中转或预留的本机转 API 端口,未配置则阻止并提示;非高峰不拦、正常走官方,专治高峰掉速与刷屏。标准可安装 dsh 插件,host 单半身,设置经 cordis 配置文件注入。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"dsh",
|
|
@@ -17,30 +17,41 @@
|
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "git+https://github.com/better-er/dsh-peak-block.git"
|
|
19
19
|
},
|
|
20
|
+
"author": "betterer",
|
|
21
|
+
"packageManager": "pnpm@9.15.9",
|
|
20
22
|
"type": "module",
|
|
21
23
|
"main": "lib/index.js",
|
|
24
|
+
"types": "lib/index.d.ts",
|
|
22
25
|
"exports": {
|
|
23
|
-
".":
|
|
24
|
-
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./lib/index.d.ts",
|
|
28
|
+
"default": "./lib/index.js"
|
|
29
|
+
},
|
|
25
30
|
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
26
31
|
"./package.json": "./package.json"
|
|
27
32
|
},
|
|
28
33
|
"dsh": {
|
|
29
34
|
"bundle": {
|
|
30
35
|
"patch": "./cordis.patch.yml"
|
|
31
|
-
},
|
|
32
|
-
"client": {
|
|
33
|
-
"platform": "web",
|
|
34
|
-
"inject": [
|
|
35
|
-
"@deepseek-ai/dsh-client-ui-slots"
|
|
36
|
-
]
|
|
37
36
|
}
|
|
38
37
|
},
|
|
39
38
|
"files": [
|
|
40
39
|
"lib/index.js",
|
|
41
|
-
"lib/
|
|
40
|
+
"lib/index.d.ts",
|
|
42
41
|
"cordis.patch.yml",
|
|
43
42
|
"docs/"
|
|
44
43
|
],
|
|
45
|
-
"license": "MIT"
|
|
46
|
-
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsdown",
|
|
47
|
+
"prepare": "tsdown",
|
|
48
|
+
"typecheck": "tsc --noEmit --pretty false",
|
|
49
|
+
"test": "vitest run"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/node": "^22.0.0",
|
|
53
|
+
"tsdown": "^0.22.2",
|
|
54
|
+
"typescript": "^5.9.3",
|
|
55
|
+
"vitest": "^4.1.1"
|
|
56
|
+
}
|
|
57
|
+
}
|
package/lib/client.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
// dsh-peak-block 浏览器半身:设置面板里的一个独立标签页。
|
|
2
|
-
// host 侧(lib/index.js)完成真正的拦截与路由切换。本半身提供配置入口的 UI 骨架:
|
|
3
|
-
// enabled、targetProvider、officialProviders、peakWindow 与预留的「转 API 端口」。
|
|
4
|
-
// 当前仅为界面演示,字段值不写回 host;正式生效需后续接线 host 的配置读写通道。
|
|
5
|
-
// 真正的「被拦截且未配置目标」提示,由 host 侧抛出的失败文案随界面失败信息呈现,本半身不重复注入,避免易碎的 UI 依赖。
|
|
6
|
-
|
|
7
|
-
window.__ModuleLoader__.load({
|
|
8
|
-
id: 'dsh-peak-block',
|
|
9
|
-
factory: (require) => {
|
|
10
|
-
const module = { exports: {} }
|
|
11
|
-
const exports = module.exports
|
|
12
|
-
|
|
13
|
-
const React = require('react')
|
|
14
|
-
|
|
15
|
-
const plugin = {
|
|
16
|
-
name: 'dsh-peak-block',
|
|
17
|
-
inject: ['slots'],
|
|
18
|
-
apply(ctx) {
|
|
19
|
-
const slots = ctx.get && ctx.get('slots')
|
|
20
|
-
if (!slots) return
|
|
21
|
-
|
|
22
|
-
const DOC = typeof document !== 'undefined' ? document : null
|
|
23
|
-
let styleTag = null
|
|
24
|
-
|
|
25
|
-
function ensureStyle() {
|
|
26
|
-
if (!DOC || styleTag) return
|
|
27
|
-
const css = [
|
|
28
|
-
'.dshpb-section{display:flex;flex-direction:column;gap:12px;max-width:720px;color:var(--dsw-alias-label-primary)}',
|
|
29
|
-
'.dshpb-title{margin:0;font-size:16px;line-height:24px;font-weight:500;color:var(--dsw-alias-label-primary)}',
|
|
30
|
-
'.dshpb-intro{margin:0;font-size:14px;line-height:22px;color:var(--dsw-alias-label-tertiary)}',
|
|
31
|
-
'.dshpb-notice{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-state-warn-label)}',
|
|
32
|
-
'.dshpb-card{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:14px}',
|
|
33
|
-
'.dshpb-field{display:flex;flex-direction:column;gap:6px}',
|
|
34
|
-
'.dshpb-fieldLabel{font-size:12px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-secondary)}',
|
|
35
|
-
'.dshpb-hint{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}',
|
|
36
|
-
'.dshpb-input{box-sizing:border-box;width:100%;height:32px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;font:inherit;font-size:14px;line-height:22px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary)}',
|
|
37
|
-
'.dshpb-input:focus{outline:none;border-color:var(--dsw-alias-brand-primary)}',
|
|
38
|
-
'.dshpb-input::placeholder{color:var(--dsw-alias-label-dimmed)}',
|
|
39
|
-
'select.dshpb-input{max-width:320px;cursor:pointer}',
|
|
40
|
-
'.dshpb-rowInput{max-width:320px}',
|
|
41
|
-
'.dshpb-actions{display:flex;justify-content:flex-end;gap:8px}',
|
|
42
|
-
'.dshpb-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:36px;padding:0 14px;border:none;border-radius:18px;font:inherit;font-size:14px;line-height:22px;cursor:pointer}',
|
|
43
|
-
'.dshpb-buttonPrimary{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}',
|
|
44
|
-
'.dshpb-buttonPrimary:hover{background:var(--dsw-alias-button-primary-hover)}',
|
|
45
|
-
'.dshpb-switch{display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:14px;line-height:22px}',
|
|
46
|
-
].join('')
|
|
47
|
-
const tag = DOC.createElement('style')
|
|
48
|
-
tag.textContent = css
|
|
49
|
-
DOC.head.appendChild(tag)
|
|
50
|
-
styleTag = tag
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const TARGET_TYPES = {
|
|
54
|
-
none: '未配置(高峰阻止并提示)',
|
|
55
|
-
proxy: '第三方中转路由',
|
|
56
|
-
port: '转 API 端口',
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function Section() {
|
|
60
|
-
const [enabled, setEnabled] = React.useState(true)
|
|
61
|
-
const [targetType, setTargetType] = React.useState('none')
|
|
62
|
-
const [proxyRoute, setProxyRoute] = React.useState('opencode-go')
|
|
63
|
-
const [apiPort, setApiPort] = React.useState('127.0.0.1:8080')
|
|
64
|
-
const [official, setOfficial] = React.useState('deepseek-official')
|
|
65
|
-
const [savedHint, setSavedHint] = React.useState('')
|
|
66
|
-
|
|
67
|
-
function onSave() {
|
|
68
|
-
setSavedHint('已保存到界面状态,尚未写回 host,正式生效需接线后端。')
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return React.createElement(
|
|
72
|
-
'div',
|
|
73
|
-
{ className: 'dshpb-section', role: 'region', 'aria-label': '梁文峰拦截设置' },
|
|
74
|
-
React.createElement('h2', { className: 'dshpb-title' }, '梁文峰时间拦截'),
|
|
75
|
-
React.createElement('p', { className: 'dshpb-intro' }, '在 DeepSeek 官方高峰时段拦截官方 provider 请求,可切换到第三方中转或本机转 API 端口。'),
|
|
76
|
-
React.createElement('p', { className: 'dshpb-notice' }, '当前为配置界面骨架,改动不写回 host,正式生效需后续接线。'),
|
|
77
|
-
React.createElement(
|
|
78
|
-
'div',
|
|
79
|
-
{ className: 'dshpb-card' },
|
|
80
|
-
React.createElement(
|
|
81
|
-
'label',
|
|
82
|
-
{ className: 'dshpb-switch' },
|
|
83
|
-
React.createElement('input', {
|
|
84
|
-
type: 'checkbox',
|
|
85
|
-
checked: enabled,
|
|
86
|
-
onChange: function (e) { setEnabled(e.target.checked) },
|
|
87
|
-
}),
|
|
88
|
-
'启用拦截'
|
|
89
|
-
),
|
|
90
|
-
React.createElement(
|
|
91
|
-
'div',
|
|
92
|
-
{ className: 'dshpb-field' },
|
|
93
|
-
React.createElement('label', { className: 'dshpb-fieldLabel', htmlFor: 'dshpb-target' }, '拦截目标 targetProvider'),
|
|
94
|
-
React.createElement(
|
|
95
|
-
'select',
|
|
96
|
-
{
|
|
97
|
-
id: 'dshpb-target',
|
|
98
|
-
className: 'dshpb-input',
|
|
99
|
-
value: targetType,
|
|
100
|
-
onChange: function (e) { setTargetType(e.target.value) },
|
|
101
|
-
},
|
|
102
|
-
React.createElement('option', { value: 'none' }, TARGET_TYPES.none),
|
|
103
|
-
React.createElement('option', { value: 'proxy' }, TARGET_TYPES.proxy),
|
|
104
|
-
React.createElement('option', { value: 'port' }, TARGET_TYPES.port)
|
|
105
|
-
),
|
|
106
|
-
targetType === 'proxy' &&
|
|
107
|
-
React.createElement('input', {
|
|
108
|
-
className: 'dshpb-input dshpb-rowInput',
|
|
109
|
-
value: proxyRoute,
|
|
110
|
-
placeholder: '如 opencode-go',
|
|
111
|
-
'aria-label': '第三方中转路由',
|
|
112
|
-
onChange: function (e) { setProxyRoute(e.target.value) },
|
|
113
|
-
}),
|
|
114
|
-
targetType === 'port' &&
|
|
115
|
-
React.createElement(
|
|
116
|
-
'div',
|
|
117
|
-
{ className: 'dshpb-field' },
|
|
118
|
-
React.createElement('label', { className: 'dshpb-fieldLabel', htmlFor: 'dshpb-port' }, '转 API 端口'),
|
|
119
|
-
React.createElement('input', {
|
|
120
|
-
id: 'dshpb-port',
|
|
121
|
-
className: 'dshpb-input dshpb-rowInput',
|
|
122
|
-
value: apiPort,
|
|
123
|
-
placeholder: '127.0.0.1:8080',
|
|
124
|
-
onChange: function (e) { setApiPort(e.target.value) },
|
|
125
|
-
}),
|
|
126
|
-
React.createElement('p', { className: 'dshpb-hint' }, '预留:把被拦请求转发到本机 OpenAI 兼容端口。')
|
|
127
|
-
)
|
|
128
|
-
),
|
|
129
|
-
React.createElement(
|
|
130
|
-
'div',
|
|
131
|
-
{ className: 'dshpb-field' },
|
|
132
|
-
React.createElement('label', { className: 'dshpb-fieldLabel', htmlFor: 'dshpb-official' }, '官方判定 provider 名单(逗号分隔)'),
|
|
133
|
-
React.createElement('input', {
|
|
134
|
-
id: 'dshpb-official',
|
|
135
|
-
className: 'dshpb-input dshpb-rowInput',
|
|
136
|
-
value: official,
|
|
137
|
-
onChange: function (e) { setOfficial(e.target.value) },
|
|
138
|
-
})
|
|
139
|
-
),
|
|
140
|
-
React.createElement(
|
|
141
|
-
'div',
|
|
142
|
-
{ className: 'dshpb-field' },
|
|
143
|
-
React.createElement('span', { className: 'dshpb-fieldLabel' }, '高峰时段'),
|
|
144
|
-
React.createElement('p', { className: 'dshpb-hint' }, '北京时间工作日 09:00–12:00、14:00–18:00,周末全天谷价。')
|
|
145
|
-
),
|
|
146
|
-
React.createElement(
|
|
147
|
-
'div',
|
|
148
|
-
{ className: 'dshpb-actions' },
|
|
149
|
-
React.createElement(
|
|
150
|
-
'button',
|
|
151
|
-
{ type: 'button', className: 'dshpb-button dshpb-buttonPrimary', onClick: onSave },
|
|
152
|
-
'保存(演示)'
|
|
153
|
-
)
|
|
154
|
-
),
|
|
155
|
-
savedHint !== '' &&
|
|
156
|
-
React.createElement('p', { className: 'dshpb-hint' }, savedHint)
|
|
157
|
-
)
|
|
158
|
-
)
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
slots.inject('settings.section', function () {
|
|
162
|
-
return slots.register(
|
|
163
|
-
{
|
|
164
|
-
name: 'settings.section',
|
|
165
|
-
id: 'peak-block',
|
|
166
|
-
order: 90,
|
|
167
|
-
label: '梁文峰拦截',
|
|
168
|
-
},
|
|
169
|
-
() => React.createElement(Section)
|
|
170
|
-
)
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
ctx.effect(function () {
|
|
174
|
-
ensureStyle()
|
|
175
|
-
return function () {
|
|
176
|
-
if (styleTag && styleTag.parentNode) styleTag.parentNode.removeChild(styleTag)
|
|
177
|
-
styleTag = null
|
|
178
|
-
}
|
|
179
|
-
})
|
|
180
|
-
},
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
exports.default = plugin
|
|
184
|
-
exports.name = plugin.name
|
|
185
|
-
exports.inject = plugin.inject
|
|
186
|
-
exports.apply = plugin.apply
|
|
187
|
-
|
|
188
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
|
|
189
|
-
return module.exports
|
|
190
|
-
},
|
|
191
|
-
})
|