@lyogvce/dsh-codex-subscription 0.2.6 → 0.2.7
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 +2 -1
- package/lib/client.js +80 -7
- package/lib/index.d.ts +12 -1
- package/lib/index.js +70 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
- 通过 `contextWindows` 按模型 `slug` 自定义向 DSH 会话与自动压缩器暴露的预算窗口(例如设为 `400000`);未配置时默认使用模型物理上限 `max_context_window`(`872000`)。同时底层 `PiCodexModel.contextWindow` 始终保持物理上限 `872000`,彻底防止 `dsh-llm-pi-ai` 在预算窗口与物理上限之间误判 `CONTEXT_WINDOW_EXCEEDED`。
|
|
21
21
|
4. **原生多轮思考链回放与工具调用**
|
|
22
22
|
- 底层复用 DSH 内置的 `PiAiAdapter` 与 `@earendil-works/pi-ai` 的 `openaiCodexProvider()`,固定使用 `sse` 传输(含 `zstd` 请求体压缩、规范化纯 UUID `session-id` / `x-client-request-id` 缓存头、`reasoning.encrypted_content` 签名回放与图片自动缩放卸载)。
|
|
23
|
-
5. **Codex
|
|
23
|
+
5. **Codex 订阅限额内联展示与 ⚡ Fast 极速模式一键切换(`conversation.input.right`)**
|
|
24
24
|
- 当会话选中 `openai-codex` 模型时,在输入框右下角紧邻模型选择器左侧展示限额胶囊(根据 `wham/usage` 返回的 `limit_window_seconds` 自适应展示 Plus 的 `5h X% · 7d Y%` 双窗口或 Pro 的 `7d X%` 单窗口,内置 60 秒缓存、单飞合并与每轮回复结束自动刷新),点击可展开查看对应窗口进度条、重置倒计时与手动刷新按钮;切换至其他 Provider 模型时自动隐藏。
|
|
25
|
+
- **⚡ Fast 极速模式(`service_tier: "priority"`,最高 2x 推理速度)**:可在限额弹窗底部直接一键开关 Fast 模式(自动持久化至 `~/.codex/cache/dsh-codex-subscription/runtime-state.json`,也可在插件设置页通过 `fastMode` 配置),开启后胶囊上显示闪电标识 `⚡`,并在请求体中自动为支持 Fast 档位的模型注入 `"service_tier": "priority"`。
|
|
25
26
|
|
|
26
27
|
---
|
|
27
28
|
|
package/lib/client.js
CHANGED
|
@@ -139,12 +139,14 @@ window.__ModuleLoader__.load({
|
|
|
139
139
|
const ttlMs = options.ttlMs ?? 6e4;
|
|
140
140
|
let state = {
|
|
141
141
|
quota: null,
|
|
142
|
+
fastMode: false,
|
|
142
143
|
loading: false,
|
|
143
144
|
error: null
|
|
144
145
|
};
|
|
145
146
|
let inflight = null;
|
|
146
147
|
let inflightForce = false;
|
|
147
148
|
let requestSeq = 0;
|
|
149
|
+
let fastModeSeq = 0;
|
|
148
150
|
const listeners = new Set();
|
|
149
151
|
function setState(next) {
|
|
150
152
|
state = next;
|
|
@@ -154,6 +156,7 @@ window.__ModuleLoader__.load({
|
|
|
154
156
|
if (!force && state.quota !== null && nowFn() - state.quota.fetchedAt < ttlMs) return;
|
|
155
157
|
if (inflight !== null && (!force || inflightForce)) return inflight;
|
|
156
158
|
const seq = ++requestSeq;
|
|
159
|
+
const fastSeqAtStart = fastModeSeq;
|
|
157
160
|
inflightForce = force;
|
|
158
161
|
setState({
|
|
159
162
|
...state,
|
|
@@ -170,7 +173,7 @@ window.__ModuleLoader__.load({
|
|
|
170
173
|
if (seq !== requestSeq) return;
|
|
171
174
|
if (!response.ok) {
|
|
172
175
|
setState({
|
|
173
|
-
|
|
176
|
+
...state,
|
|
174
177
|
loading: false,
|
|
175
178
|
error: `HTTP ${response.status}`
|
|
176
179
|
});
|
|
@@ -178,8 +181,10 @@ window.__ModuleLoader__.load({
|
|
|
178
181
|
}
|
|
179
182
|
const payload = await response.json();
|
|
180
183
|
if (seq !== requestSeq) return;
|
|
184
|
+
const resolvedFastMode = fastSeqAtStart === fastModeSeq && typeof payload?.fastMode === "boolean" ? payload.fastMode : state.fastMode;
|
|
181
185
|
if (payload && payload.ok === true && payload.quota) setState({
|
|
182
186
|
quota: payload.quota,
|
|
187
|
+
fastMode: resolvedFastMode,
|
|
183
188
|
loading: false,
|
|
184
189
|
error: null
|
|
185
190
|
});
|
|
@@ -188,6 +193,7 @@ window.__ModuleLoader__.load({
|
|
|
188
193
|
const errMsg = payload && !payload.ok && typeof payload.error === "string" ? payload.error : "Invalid response";
|
|
189
194
|
setState({
|
|
190
195
|
quota: fallbackQuota ?? null,
|
|
196
|
+
fastMode: resolvedFastMode,
|
|
191
197
|
loading: false,
|
|
192
198
|
error: errMsg
|
|
193
199
|
});
|
|
@@ -195,7 +201,7 @@ window.__ModuleLoader__.load({
|
|
|
195
201
|
} catch (error) {
|
|
196
202
|
if (seq !== requestSeq) return;
|
|
197
203
|
setState({
|
|
198
|
-
|
|
204
|
+
...state,
|
|
199
205
|
loading: false,
|
|
200
206
|
error: safeErrorMessage(error)
|
|
201
207
|
});
|
|
@@ -209,6 +215,30 @@ window.__ModuleLoader__.load({
|
|
|
209
215
|
inflight = task;
|
|
210
216
|
return task;
|
|
211
217
|
}
|
|
218
|
+
async function setFastMode(enabled) {
|
|
219
|
+
const nextFastMode = Boolean(enabled);
|
|
220
|
+
const seq = ++fastModeSeq;
|
|
221
|
+
setState({
|
|
222
|
+
...state,
|
|
223
|
+
fastMode: nextFastMode
|
|
224
|
+
});
|
|
225
|
+
try {
|
|
226
|
+
const response = await fetchFn(USAGE_ROUTE_PATH, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: {
|
|
229
|
+
"Content-Type": "application/json",
|
|
230
|
+
Accept: "application/json"
|
|
231
|
+
},
|
|
232
|
+
body: JSON.stringify({ fastMode: nextFastMode })
|
|
233
|
+
});
|
|
234
|
+
if (seq !== fastModeSeq || !response.ok) return;
|
|
235
|
+
const payload = await response.json();
|
|
236
|
+
if (seq === fastModeSeq && typeof payload?.fastMode === "boolean") setState({
|
|
237
|
+
...state,
|
|
238
|
+
fastMode: payload.fastMode
|
|
239
|
+
});
|
|
240
|
+
} catch {}
|
|
241
|
+
}
|
|
212
242
|
return {
|
|
213
243
|
getSnapshot: () => state,
|
|
214
244
|
subscribe(listener) {
|
|
@@ -217,7 +247,8 @@ window.__ModuleLoader__.load({
|
|
|
217
247
|
listeners.delete(listener);
|
|
218
248
|
};
|
|
219
249
|
},
|
|
220
|
-
refresh
|
|
250
|
+
refresh,
|
|
251
|
+
setFastMode
|
|
221
252
|
};
|
|
222
253
|
}
|
|
223
254
|
//#endregion
|
|
@@ -250,6 +281,15 @@ window.__ModuleLoader__.load({
|
|
|
250
281
|
".dsh-codex-quota-bar-fill[data-level=\"warn\"]{background:var(--dsw-alias-state-warn-primary)}",
|
|
251
282
|
".dsh-codex-quota-bar-fill[data-level=\"error\"]{background:var(--dsw-alias-state-error-primary)}",
|
|
252
283
|
".dsh-codex-quota-meta{display:flex;justify-content:space-between;gap:8px;font-size:11px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums}",
|
|
284
|
+
".dsh-codex-quota-fast-bolt{color:var(--dsw-alias-state-warn-primary,#f59e0b);flex:none}",
|
|
285
|
+
".dsh-codex-fast-row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:8px;padding-top:8px;border-top:1px solid var(--dsw-alias-border-l1)}",
|
|
286
|
+
".dsh-codex-fast-info{display:flex;flex-direction:column;gap:1px;min-width:0}",
|
|
287
|
+
".dsh-codex-fast-title{display:inline-flex;align-items:center;gap:4px;font-size:12px;font-weight:500;color:var(--dsw-alias-label-primary)}",
|
|
288
|
+
".dsh-codex-fast-desc{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary)}",
|
|
289
|
+
".dsh-codex-fast-switch{position:relative;width:32px;height:18px;border-radius:999px;border:none;padding:0;background:var(--dsw-alias-border-l2,rgba(128,128,128,0.35));cursor:pointer;flex:none;transition:background-color .15s ease}",
|
|
290
|
+
".dsh-codex-fast-switch[data-checked=\"true\"]{background:var(--dsw-alias-brand-primary,#3b82f6)}",
|
|
291
|
+
".dsh-codex-fast-switch-thumb{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;background:#fff;box-shadow:0 1px 2px rgba(0,0,0,0.2);transition:transform .15s ease}",
|
|
292
|
+
".dsh-codex-fast-switch[data-checked=\"true\"] .dsh-codex-fast-switch-thumb{transform:translateX(14px)}",
|
|
253
293
|
".dsh-codex-quota-error{margin-top:6px;padding:4px 6px;border-radius:4px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-state-error-primary);font-size:11px;word-break:break-word}"
|
|
254
294
|
].join("");
|
|
255
295
|
//#endregion
|
|
@@ -265,7 +305,9 @@ window.__ModuleLoader__.load({
|
|
|
265
305
|
"quota.refresh": "↻ 刷新",
|
|
266
306
|
"quota.refreshing": "↻ 刷新中…",
|
|
267
307
|
"quota.usedPrefix": "已用 ",
|
|
268
|
-
"quota.remainPrefix": "剩余 "
|
|
308
|
+
"quota.remainPrefix": "剩余 ",
|
|
309
|
+
"quota.fastTitle": "Fast 极速模式",
|
|
310
|
+
"quota.fastDesc": "2x 推理速度,消耗更多限额"
|
|
269
311
|
};
|
|
270
312
|
const en = {
|
|
271
313
|
"quota.title": "Codex Subscription Quota",
|
|
@@ -273,7 +315,9 @@ window.__ModuleLoader__.load({
|
|
|
273
315
|
"quota.refresh": "↻ Refresh",
|
|
274
316
|
"quota.refreshing": "↻ Refreshing…",
|
|
275
317
|
"quota.usedPrefix": "Used ",
|
|
276
|
-
"quota.remainPrefix": "Remaining "
|
|
318
|
+
"quota.remainPrefix": "Remaining ",
|
|
319
|
+
"quota.fastTitle": "Fast Mode",
|
|
320
|
+
"quota.fastDesc": "2x speed, increased usage"
|
|
277
321
|
};
|
|
278
322
|
const EMPTY_MODEL_STORE = {
|
|
279
323
|
getSnapshot: () => null,
|
|
@@ -354,6 +398,7 @@ window.__ModuleLoader__.load({
|
|
|
354
398
|
type: "button",
|
|
355
399
|
className: "dsh-codex-quota-trigger",
|
|
356
400
|
"data-level": level,
|
|
401
|
+
"data-fast": quotaSnap.fastMode ? "true" : void 0,
|
|
357
402
|
"aria-label": `${t("quota.title")}: ${pillText}`,
|
|
358
403
|
"aria-haspopup": "dialog",
|
|
359
404
|
"aria-expanded": open,
|
|
@@ -380,7 +425,16 @@ window.__ModuleLoader__.load({
|
|
|
380
425
|
r: 5.5,
|
|
381
426
|
strokeDasharray: ringDash,
|
|
382
427
|
transform: "rotate(-90 7 7)"
|
|
383
|
-
})),
|
|
428
|
+
})), quotaSnap.fastMode ? h("svg", {
|
|
429
|
+
className: "dsh-codex-quota-fast-bolt",
|
|
430
|
+
viewBox: "0 0 12 12",
|
|
431
|
+
width: 11,
|
|
432
|
+
height: 11,
|
|
433
|
+
"aria-hidden": true
|
|
434
|
+
}, h("path", {
|
|
435
|
+
d: "M6.8 1L2.5 6.8h3.1L4.8 11l4.7-5.8H6.4L6.8 1z",
|
|
436
|
+
fill: "currentColor"
|
|
437
|
+
})) : null, h("span", { className: "dsh-codex-quota-text" }, pillText)), open ? h("div", {
|
|
384
438
|
className: "dsh-codex-quota-popover",
|
|
385
439
|
role: "dialog",
|
|
386
440
|
"aria-label": t("quota.title"),
|
|
@@ -394,7 +448,26 @@ window.__ModuleLoader__.load({
|
|
|
394
448
|
onClick: () => {
|
|
395
449
|
quotaStore.refresh(true);
|
|
396
450
|
}
|
|
397
|
-
}, quotaSnap.loading ? t("quota.refreshing") : t("quota.refresh"))), quota !== null ? renderWindowSection(formatWindowTitle(quota.primary.windowSeconds, locale, DEFAULT_PRIMARY_WINDOW_SECONDS), quota.primary, primaryReached) : quotaSnap.loading || quotaSnap.error === null ? h("div", { className: "dsh-codex-quota-meta" }, t("quota.loading")) : null, quota?.secondary !== null && quota?.secondary !== void 0 ? renderWindowSection(formatWindowTitle(quota.secondary.windowSeconds, locale, DEFAULT_SECONDARY_WINDOW_SECONDS), quota.secondary, secondaryReached) : null,
|
|
451
|
+
}, quotaSnap.loading ? t("quota.refreshing") : t("quota.refresh"))), quota !== null ? renderWindowSection(formatWindowTitle(quota.primary.windowSeconds, locale, DEFAULT_PRIMARY_WINDOW_SECONDS), quota.primary, primaryReached) : quotaSnap.loading || quotaSnap.error === null ? h("div", { className: "dsh-codex-quota-meta" }, t("quota.loading")) : null, quota?.secondary !== null && quota?.secondary !== void 0 ? renderWindowSection(formatWindowTitle(quota.secondary.windowSeconds, locale, DEFAULT_SECONDARY_WINDOW_SECONDS), quota.secondary, secondaryReached) : null, h("div", { className: "dsh-codex-fast-row" }, h("div", { className: "dsh-codex-fast-info" }, h("div", { className: "dsh-codex-fast-title" }, h("svg", {
|
|
452
|
+
className: "dsh-codex-quota-fast-bolt",
|
|
453
|
+
viewBox: "0 0 12 12",
|
|
454
|
+
width: 12,
|
|
455
|
+
height: 12,
|
|
456
|
+
"aria-hidden": true
|
|
457
|
+
}, h("path", {
|
|
458
|
+
d: "M6.8 1L2.5 6.8h3.1L4.8 11l4.7-5.8H6.4L6.8 1z",
|
|
459
|
+
fill: "currentColor"
|
|
460
|
+
})), h("span", null, t("quota.fastTitle"))), h("div", { className: "dsh-codex-fast-desc" }, t("quota.fastDesc"))), h("button", {
|
|
461
|
+
type: "button",
|
|
462
|
+
role: "switch",
|
|
463
|
+
className: "dsh-codex-fast-switch",
|
|
464
|
+
"aria-checked": quotaSnap.fastMode,
|
|
465
|
+
"aria-label": t("quota.fastTitle"),
|
|
466
|
+
"data-checked": quotaSnap.fastMode ? "true" : "false",
|
|
467
|
+
onClick: () => {
|
|
468
|
+
quotaStore.setFastMode(!quotaSnap.fastMode);
|
|
469
|
+
}
|
|
470
|
+
}, h("span", { className: "dsh-codex-fast-switch-thumb" }))), quotaSnap.error !== null ? h("div", { className: "dsh-codex-quota-error" }, quotaSnap.error) : null) : null);
|
|
398
471
|
}
|
|
399
472
|
const inject = ["slots", "locale"];
|
|
400
473
|
function apply(ctx) {
|
package/lib/index.d.ts
CHANGED
|
@@ -61,10 +61,16 @@ declare function createAuthManager(options: AuthManagerOptions): {
|
|
|
61
61
|
//#region src/catalog.d.ts
|
|
62
62
|
export declare const PLUGIN_CACHE_DIR_NAME = "cache/dsh-codex-subscription";
|
|
63
63
|
export declare const BACKUP_CACHE_FILE_NAME = "models-cache-backup.json";
|
|
64
|
+
export declare const RUNTIME_STATE_FILE_NAME = "runtime-state.json";
|
|
64
65
|
interface RawReasoningLevel {
|
|
65
66
|
effort?: string;
|
|
66
67
|
description?: string;
|
|
67
68
|
}
|
|
69
|
+
interface RawServiceTier {
|
|
70
|
+
id?: string;
|
|
71
|
+
name?: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
}
|
|
68
74
|
interface RawCodexModelEntry {
|
|
69
75
|
slug?: string;
|
|
70
76
|
display_name?: string;
|
|
@@ -78,6 +84,8 @@ interface RawCodexModelEntry {
|
|
|
78
84
|
default_reasoning_level?: string;
|
|
79
85
|
support_verbosity?: boolean;
|
|
80
86
|
default_verbosity?: string;
|
|
87
|
+
additional_speed_tiers?: unknown[];
|
|
88
|
+
service_tiers?: RawServiceTier[];
|
|
81
89
|
}
|
|
82
90
|
interface RawModelsCache {
|
|
83
91
|
fetched_at?: string;
|
|
@@ -91,6 +99,7 @@ interface CodexModelMetadata {
|
|
|
91
99
|
readonly supportVerbosity: boolean;
|
|
92
100
|
readonly defaultVerbosity: 'low' | 'medium' | 'high';
|
|
93
101
|
readonly defaultReasoningLevel?: string | undefined;
|
|
102
|
+
readonly fastServiceTier?: string | undefined;
|
|
94
103
|
}
|
|
95
104
|
interface ParsedModelsCatalog {
|
|
96
105
|
readonly clientVersion: string;
|
|
@@ -104,7 +113,7 @@ declare function parseModelsCache(rawCache: RawModelsCache | null | undefined, b
|
|
|
104
113
|
declare function buildCodexUserAgent(version?: string): string;
|
|
105
114
|
declare function normalizeCodexSessionId(sessionId: string | undefined): string | undefined;
|
|
106
115
|
declare function buildCodexHeaders(existingHeaders?: Record<string, string> | undefined, clientVersion?: string): Record<string, string>;
|
|
107
|
-
declare function sanitizeCodexPayload(payload: Record<string, unknown> | null | undefined, modelMeta?: CodexModelMetadata | undefined): Record<string, unknown> | null | undefined;
|
|
116
|
+
declare function sanitizeCodexPayload(payload: Record<string, unknown> | null | undefined, modelMeta?: CodexModelMetadata | undefined, fastMode?: boolean): Record<string, unknown> | null | undefined;
|
|
108
117
|
//#endregion
|
|
109
118
|
//#region src/usage.d.ts
|
|
110
119
|
declare function safeErrorMessage(error: unknown): string;
|
|
@@ -123,10 +132,12 @@ interface CodexQuotaSnapshot {
|
|
|
123
132
|
type CodexQuotaResponse = {
|
|
124
133
|
readonly ok: true;
|
|
125
134
|
readonly quota: CodexQuotaSnapshot;
|
|
135
|
+
readonly fastMode?: boolean | undefined;
|
|
126
136
|
} | {
|
|
127
137
|
readonly ok: false;
|
|
128
138
|
readonly error: string;
|
|
129
139
|
readonly quota?: CodexQuotaSnapshot | undefined;
|
|
140
|
+
readonly fastMode?: boolean | undefined;
|
|
130
141
|
};
|
|
131
142
|
interface UsageFetcherOptions {
|
|
132
143
|
readonly resolveCredentials: () => Promise<CodexResolvedCredentials>;
|
package/lib/index.js
CHANGED
|
@@ -15,6 +15,7 @@ const DEFAULT_CLIENT_VERSION = "0.163.0";
|
|
|
15
15
|
const DEFAULT_CONTEXT_WINDOW = 872e3;
|
|
16
16
|
const PLUGIN_CACHE_DIR_NAME = "cache/dsh-codex-subscription";
|
|
17
17
|
const BACKUP_CACHE_FILE_NAME = "models-cache-backup.json";
|
|
18
|
+
const RUNTIME_STATE_FILE_NAME = "runtime-state.json";
|
|
18
19
|
const THINKING_LEVELS = [
|
|
19
20
|
"off",
|
|
20
21
|
"minimal",
|
|
@@ -81,6 +82,16 @@ function isVerbosity(value) {
|
|
|
81
82
|
function isPositiveSafeInt(value) {
|
|
82
83
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
83
84
|
}
|
|
85
|
+
function resolveFastServiceTier(entry) {
|
|
86
|
+
if (Array.isArray(entry.service_tiers)) {
|
|
87
|
+
const matched = entry.service_tiers.find((tier) => tier !== null && typeof tier === "object" && typeof tier.id === "string" && tier.id.trim() !== "" && (tier.id.trim() === "priority" || typeof tier.name === "string" && tier.name.trim().toLowerCase() === "fast"));
|
|
88
|
+
if (matched && typeof matched.id === "string") return matched.id.trim();
|
|
89
|
+
if (entry.service_tiers.length === 0 && Array.isArray(entry.additional_speed_tiers)) return entry.additional_speed_tiers.some((t) => typeof t === "string" && t.trim().toLowerCase() === "fast") ? "priority" : void 0;
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (Array.isArray(entry.additional_speed_tiers) && entry.additional_speed_tiers.some((t) => typeof t === "string" && t.trim().toLowerCase() === "fast")) return "priority";
|
|
93
|
+
return "priority";
|
|
94
|
+
}
|
|
84
95
|
function parseModelsCache(rawCache, baseModels, disabledModels = [], contextWindows = {}) {
|
|
85
96
|
const disabledSet = new Set((Array.isArray(disabledModels) ? disabledModels : []).filter((id) => typeof id === "string").map((id) => id.trim()).filter(Boolean));
|
|
86
97
|
const clientVersion = typeof rawCache?.client_version === "string" && rawCache.client_version.trim() !== "" ? rawCache.client_version.trim() : DEFAULT_CLIENT_VERSION;
|
|
@@ -108,11 +119,13 @@ function parseModelsCache(rawCache, baseModels, disabledModels = [], contextWind
|
|
|
108
119
|
const supportVerbosity = entry.support_verbosity !== false;
|
|
109
120
|
const defaultVerbosity = isVerbosity(entry.default_verbosity) ? entry.default_verbosity : "low";
|
|
110
121
|
const defaultReasoningLevel = normalizeDefaultReasoningLevel(entry.default_reasoning_level, thinkingLevelMap);
|
|
122
|
+
const fastServiceTier = resolveFastServiceTier(entry);
|
|
111
123
|
metadataById.set(id, {
|
|
112
124
|
contextWindow: targetContextWindow,
|
|
113
125
|
supportVerbosity,
|
|
114
126
|
defaultVerbosity,
|
|
115
|
-
defaultReasoningLevel
|
|
127
|
+
defaultReasoningLevel,
|
|
128
|
+
fastServiceTier
|
|
116
129
|
});
|
|
117
130
|
models.push({
|
|
118
131
|
...base ?? {},
|
|
@@ -197,11 +210,15 @@ function buildCodexHeaders(existingHeaders, clientVersion = DEFAULT_CLIENT_VERSI
|
|
|
197
210
|
headers["User-Agent"] = buildCodexUserAgent(clientVersion);
|
|
198
211
|
return headers;
|
|
199
212
|
}
|
|
200
|
-
function sanitizeCodexPayload(payload, modelMeta) {
|
|
213
|
+
function sanitizeCodexPayload(payload, modelMeta, fastMode = false) {
|
|
201
214
|
if (!payload || typeof payload !== "object") return payload;
|
|
202
215
|
const cleaned = { ...payload };
|
|
203
216
|
for (const field of FORBIDDEN_PAYLOAD_FIELDS) delete cleaned[field];
|
|
204
|
-
if (
|
|
217
|
+
if (fastMode) {
|
|
218
|
+
const tier = modelMeta === void 0 ? "priority" : modelMeta.fastServiceTier;
|
|
219
|
+
if (typeof tier === "string" && tier !== "") cleaned.service_tier = tier;
|
|
220
|
+
else delete cleaned.service_tier;
|
|
221
|
+
} else if (cleaned.service_tier !== "priority" && cleaned.service_tier !== "ultrafast") delete cleaned.service_tier;
|
|
205
222
|
const currentText = cleaned.text && typeof cleaned.text === "object" && !Array.isArray(cleaned.text) ? { ...cleaned.text } : void 0;
|
|
206
223
|
if (modelMeta?.supportVerbosity === false) {
|
|
207
224
|
if (currentText !== void 0) {
|
|
@@ -613,7 +630,21 @@ function writeBackupCache(codexHome, raw) {
|
|
|
613
630
|
writeFileAtomicSync(join(resolvePluginCacheDir(codexHome), BACKUP_CACHE_FILE_NAME), `${JSON.stringify(raw, null, 2)}\n`);
|
|
614
631
|
} catch {}
|
|
615
632
|
}
|
|
633
|
+
function readPersistedFastMode(codexHome) {
|
|
634
|
+
try {
|
|
635
|
+
const raw = JSON.parse(readFileSync(join(resolvePluginCacheDir(codexHome), RUNTIME_STATE_FILE_NAME), "utf8"));
|
|
636
|
+
return typeof raw?.fastMode === "boolean" ? raw.fastMode : void 0;
|
|
637
|
+
} catch {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
function writePersistedFastMode(codexHome, fastMode) {
|
|
642
|
+
try {
|
|
643
|
+
writeFileAtomicSync(join(resolvePluginCacheDir(codexHome), RUNTIME_STATE_FILE_NAME), `${JSON.stringify({ fastMode }, null, 2)}\n`);
|
|
644
|
+
} catch {}
|
|
645
|
+
}
|
|
616
646
|
const Config = z.object({
|
|
647
|
+
fastMode: z.boolean().default(false).description("开启 Fast 极速模式(注入 service_tier: priority,最高 2x 推理速度,消耗更多订阅限额)").volatile(),
|
|
617
648
|
disabledModels: z.array(z.string()).default([...DEFAULT_DISABLED_MODELS]).description("在模型选择器中屏蔽的模型 slug 列表").volatile(),
|
|
618
649
|
contextWindows: z.dict(z.number()).default({}).description("按模型 slug 覆盖上下文窗口大小(tokens),未配置时默认使用 872000").volatile()
|
|
619
650
|
});
|
|
@@ -651,8 +682,21 @@ function apply(ctx, config = {}) {
|
|
|
651
682
|
const baseModels = baseProvider.getModels();
|
|
652
683
|
const retryPolicy = resolveRetryPolicy(void 0, "codex-subscription retryPolicy");
|
|
653
684
|
const getCodexHome = () => resolveCodexHome();
|
|
685
|
+
const getConfigFastMode = () => Boolean(readVolatile(config.fastMode, false));
|
|
654
686
|
const getDisabledModels = () => readVolatile(config.disabledModels, DEFAULT_DISABLED_MODELS);
|
|
655
687
|
const getContextWindows = () => readVolatile(config.contextWindows, {});
|
|
688
|
+
let lastConfigFastMode = getConfigFastMode();
|
|
689
|
+
let fastModeOverride = readPersistedFastMode(getCodexHome());
|
|
690
|
+
const getEffectiveFastMode = () => fastModeOverride !== void 0 ? fastModeOverride : getConfigFastMode();
|
|
691
|
+
const setFastModeOverride = (nextFastMode, source) => {
|
|
692
|
+
const codexHome = getCodexHome();
|
|
693
|
+
fastModeOverride = nextFastMode;
|
|
694
|
+
writePersistedFastMode(codexHome, nextFastMode);
|
|
695
|
+
appendPluginLog(codexHome, ctx, "INFO", "fast_mode.updated", {
|
|
696
|
+
fastMode: nextFastMode,
|
|
697
|
+
source
|
|
698
|
+
});
|
|
699
|
+
};
|
|
656
700
|
const authManager = createAuthManager({
|
|
657
701
|
getCodexHome,
|
|
658
702
|
getClientVersion: () => ensureSnapshot().catalog.clientVersion,
|
|
@@ -690,7 +734,7 @@ function apply(ctx, config = {}) {
|
|
|
690
734
|
transport: "sse",
|
|
691
735
|
headers: buildCodexHeaders(rest.headers, catalog.clientVersion),
|
|
692
736
|
async onPayload(payload, requestModel) {
|
|
693
|
-
return sanitizeCodexPayload(await onPayload?.(payload, requestModel) ?? payload, modelMeta);
|
|
737
|
+
return sanitizeCodexPayload(await onPayload?.(payload, requestModel) ?? payload, modelMeta, getEffectiveFastMode());
|
|
694
738
|
}
|
|
695
739
|
};
|
|
696
740
|
}
|
|
@@ -854,12 +898,26 @@ function apply(ctx, config = {}) {
|
|
|
854
898
|
if (typeof registerFetch !== "function") return;
|
|
855
899
|
const registerRoute = () => registerFetch.call(connection?.fetch, {
|
|
856
900
|
path: USAGE_ROUTE_PATH,
|
|
857
|
-
methods: ["GET"],
|
|
901
|
+
methods: ["GET", "POST"],
|
|
858
902
|
requestBody: "buffered",
|
|
859
903
|
fetch: async (request) => {
|
|
904
|
+
if (request.method === "POST") {
|
|
905
|
+
const body = await request.json().catch(() => null);
|
|
906
|
+
if (typeof body?.fastMode === "boolean") setFastModeOverride(body.fastMode, "ui");
|
|
907
|
+
return new Response(JSON.stringify({
|
|
908
|
+
ok: true,
|
|
909
|
+
fastMode: getEffectiveFastMode()
|
|
910
|
+
}), {
|
|
911
|
+
status: 200,
|
|
912
|
+
headers: { "content-type": "application/json" }
|
|
913
|
+
});
|
|
914
|
+
}
|
|
860
915
|
const force = new URL(request.url, "http://localhost").searchParams.get("force") === "1";
|
|
861
916
|
const result = await usageFetcher.getUsage(force);
|
|
862
|
-
return new Response(JSON.stringify(
|
|
917
|
+
return new Response(JSON.stringify({
|
|
918
|
+
...result,
|
|
919
|
+
fastMode: getEffectiveFastMode()
|
|
920
|
+
}), {
|
|
863
921
|
status: 200,
|
|
864
922
|
headers: { "content-type": "application/json" }
|
|
865
923
|
});
|
|
@@ -871,6 +929,11 @@ function apply(ctx, config = {}) {
|
|
|
871
929
|
if (typeof ctx.inject === "function") ctx.inject(["connection"], mountUsageRoute);
|
|
872
930
|
else mountUsageRoute(ctx);
|
|
873
931
|
ctx.on?.("loader/volatile-update", () => {
|
|
932
|
+
const currentConfigFastMode = getConfigFastMode();
|
|
933
|
+
if (currentConfigFastMode !== lastConfigFastMode) {
|
|
934
|
+
lastConfigFastMode = currentConfigFastMode;
|
|
935
|
+
setFastModeOverride(currentConfigFastMode, "config");
|
|
936
|
+
}
|
|
874
937
|
ensureSnapshot();
|
|
875
938
|
ctx.llm.emitAdaptersUpdated?.();
|
|
876
939
|
});
|
|
@@ -900,4 +963,4 @@ const __test = Object.freeze({
|
|
|
900
963
|
createUsageFetcher
|
|
901
964
|
});
|
|
902
965
|
//#endregion
|
|
903
|
-
export { BACKUP_CACHE_FILE_NAME, Config, LOG_FILE_NAME, PLUGIN_CACHE_DIR_NAME, __test, apply, inject, name };
|
|
966
|
+
export { BACKUP_CACHE_FILE_NAME, Config, LOG_FILE_NAME, PLUGIN_CACHE_DIR_NAME, RUNTIME_STATE_FILE_NAME, __test, apply, inject, name };
|