dsh-live-compact 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -35
- package/adapter.js +16 -0
- package/client.js +145 -0
- package/cooldown.js +21 -5
- package/cordis.patch.yml +5 -9
- package/gate.js +73 -46
- package/index.js +351 -126
- package/package.json +22 -6
- package/plan.js +116 -27
- package/raw.js +52 -15
- package/settings.js +136 -0
- package/shadow.js +802 -81
- package/shell.js +38 -28
package/settings.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/** 设置系统 */
|
|
2
|
+
|
|
3
|
+
/** 用户可调项。默认值与 1.1.1 的行为完全一致:不配置就一切照旧。 */
|
|
4
|
+
export const DEFAULTS = Object.freeze({
|
|
5
|
+
minTokens: 20000, // 轮后小切的起点
|
|
6
|
+
keepRounds: 3, // 原处保留的最近已结束工具轮数
|
|
7
|
+
keepRoundTokens: 32000, // 云端收尾停止线
|
|
8
|
+
pointerTokens: 0, // 0 = 跟随 chunkMax
|
|
9
|
+
chunkMin: 1000, // 不够这一刀就不切
|
|
10
|
+
chunkMax: 32000, // 一刀上限,等于卡片轨道
|
|
11
|
+
bigGate: 650000, // 绝对停切线
|
|
12
|
+
cooldownMs: 0, // 0 = 只用 seq 一次性跳过
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/** 旧的环境变量仍然有效,但只作为 config 缺席时的兜底。 */
|
|
16
|
+
export const ENV = Object.freeze({
|
|
17
|
+
minTokens: "SPINE_MIN_TOKENS",
|
|
18
|
+
keepRounds: "SPINE_KEEP_ROUNDS",
|
|
19
|
+
keepRoundTokens: "SPINE_KEEP_ROUND_TOKENS",
|
|
20
|
+
pointerTokens: "SPINE_POINTER_TOKENS",
|
|
21
|
+
chunkMin: "SPINE_CHUNK_MIN",
|
|
22
|
+
chunkMax: "SPINE_CHUNK_MAX",
|
|
23
|
+
cooldownMs: "SPINE_COOLDOWN_MS",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// volatile 字段的现值是 cosmokit 的取值对象,读之前先摊平。
|
|
27
|
+
const plain = (value) => (value && typeof value === "object" && typeof value.get === "function" ? value.get() : value);
|
|
28
|
+
|
|
29
|
+
/** 预设加载时取得独立快照;未配置的 volatile 引用不能覆盖较低优先级的值。 */
|
|
30
|
+
export function configSnapshot(config = {}) {
|
|
31
|
+
return Object.fromEntries(Object.entries(config ?? {}).flatMap(([key, value]) => {
|
|
32
|
+
const current = plain(value);
|
|
33
|
+
return current == null ? [] : [[key, current]];
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const ENGINE_DEFAULTS = Object.freeze({
|
|
38
|
+
auto: true,
|
|
39
|
+
thresholdRatio: 0.8,
|
|
40
|
+
headroomTokens: 65536,
|
|
41
|
+
retainTokens: 8192,
|
|
42
|
+
compactionRetries: 0,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const ENGINE_ENV = Object.freeze({
|
|
46
|
+
thresholdRatio: "SPINE_THRESHOLD_RATIO",
|
|
47
|
+
headroomTokens: "SPINE_HEADROOM_TOKENS",
|
|
48
|
+
retainTokens: "SPINE_RETAIN_TOKENS",
|
|
49
|
+
});
|
|
50
|
+
const PLUGIN_KEYS = new Set(["takeoverPresets", ...Object.keys(DEFAULTS)]);
|
|
51
|
+
|
|
52
|
+
/** 只剥离本插件字段;其余字段(包括未知键)交给实际安装的 stock 校验。 */
|
|
53
|
+
export function engineConfig(config = {}, env = process.env) {
|
|
54
|
+
const next = {};
|
|
55
|
+
for (const [key, value] of Object.entries(config ?? {})) {
|
|
56
|
+
if (!PLUGIN_KEYS.has(key)) next[key] = plain(value);
|
|
57
|
+
}
|
|
58
|
+
for (const [key, fallback] of Object.entries(ENGINE_DEFAULTS)) {
|
|
59
|
+
// retainRatio / retainTokens 是宿主互斥选项,不能用默认 token 数盖掉显式比例。
|
|
60
|
+
if (key === "retainTokens" && next.retainRatio != null) continue;
|
|
61
|
+
const envName = ENGINE_ENV[key];
|
|
62
|
+
if (next[key] == null) next[key] = finite(envName ? env?.[envName] : undefined) ?? fallback;
|
|
63
|
+
}
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const finite = (value) => {
|
|
68
|
+
const source = plain(value);
|
|
69
|
+
if (typeof source !== "number" && typeof source !== "string") return undefined;
|
|
70
|
+
if (typeof source === "string" && !source.trim()) return undefined;
|
|
71
|
+
const number = Number(source);
|
|
72
|
+
return Number.isFinite(number) ? number : undefined;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export function readSettings(config = {}, env = process.env) {
|
|
76
|
+
const next = {};
|
|
77
|
+
for (const [key, fallback] of Object.entries(DEFAULTS)) {
|
|
78
|
+
const envName = ENV[key];
|
|
79
|
+
const numeric = (value) => {
|
|
80
|
+
const number = finite(value);
|
|
81
|
+
return key === "keepRounds" && (!Number.isInteger(number) || number < 1) ? undefined : number;
|
|
82
|
+
};
|
|
83
|
+
next[key] =
|
|
84
|
+
numeric(config?.[key]) ?? numeric(envName ? env?.[envName] : undefined) ?? fallback;
|
|
85
|
+
}
|
|
86
|
+
if (!next.pointerTokens) next.pointerTokens = next.chunkMax;
|
|
87
|
+
return Object.freeze(next);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 复用实际安装的 stock schema,保留宿主字段和校验,不修改原 schema。
|
|
92
|
+
* 每个字段都要 .volatile():宿主的设置界面只列出 volatile 字段,漏一个就整份表单不出现。
|
|
93
|
+
* 数值字段不注入 schema 默认值,保留“未显式配置”的状态,运行时才解析环境变量和默认值。
|
|
94
|
+
*/
|
|
95
|
+
export function configSchema(z, stockSchema) {
|
|
96
|
+
const count = (min) => z.number().step(1).min(min);
|
|
97
|
+
const stockFields = stockSchema?.dict ?? {
|
|
98
|
+
auto: z.boolean(),
|
|
99
|
+
thresholdRatio: z.number(),
|
|
100
|
+
headroomTokens: count(0),
|
|
101
|
+
retainTokens: count(0),
|
|
102
|
+
compactionRetries: count(0),
|
|
103
|
+
};
|
|
104
|
+
return z.object({
|
|
105
|
+
...Object.fromEntries(Object.entries(stockFields).map(([key, field]) => [
|
|
106
|
+
key, field.meta?.volatile ? field : z(field).volatile(),
|
|
107
|
+
])),
|
|
108
|
+
takeoverPresets: z.boolean().default(true)
|
|
109
|
+
.description("接管官方预设中的压缩引擎;修改后只影响新加载的预设,已有会话需重建。")
|
|
110
|
+
.volatile(),
|
|
111
|
+
minTokens: count(0)
|
|
112
|
+
.description("轮后小切的起点,上下文到这个 token 数才切一刀。")
|
|
113
|
+
.volatile(),
|
|
114
|
+
keepRounds: count(1)
|
|
115
|
+
.description("最近几个已结束的工具轮留在原文,至少 1 轮,默认 3。")
|
|
116
|
+
.volatile(),
|
|
117
|
+
keepRoundTokens: count(0)
|
|
118
|
+
.description("云端收尾停止线,降到这个 token 数以下就不再切。")
|
|
119
|
+
.volatile(),
|
|
120
|
+
pointerTokens: count(0)
|
|
121
|
+
.description("单个工具结果超过这个 token 数只留指针;0 = 跟随 chunkMax。")
|
|
122
|
+
.volatile(),
|
|
123
|
+
chunkMin: count(0)
|
|
124
|
+
.description("一刀的最小量,不够就不切,避免每轮一张碎卡片。")
|
|
125
|
+
.volatile(),
|
|
126
|
+
chunkMax: count(1)
|
|
127
|
+
.description("一刀的最大量,和卡片轨道同为 32000。")
|
|
128
|
+
.volatile(),
|
|
129
|
+
bigGate: count(1)
|
|
130
|
+
.description("绝对停切线,超过就不再做小切,交给官方大压缩。")
|
|
131
|
+
.volatile(),
|
|
132
|
+
cooldownMs: count(0)
|
|
133
|
+
.description("官方压缩之后跳过小切的时间窗;0 = 只按事件序号跳过一次。")
|
|
134
|
+
.volatile(),
|
|
135
|
+
});
|
|
136
|
+
}
|