dsh-codex-community 0.0.2 → 0.0.3
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.md +30 -0
- package/README.en.md +7 -5
- package/README.md +7 -5
- package/dist/client/index.js +300 -56
- package/docs/README.en.md +2 -0
- package/docs/README.md +2 -0
- package/docs/compatibility.en.md +10 -9
- package/docs/compatibility.md +10 -9
- package/docs/releases/v0.0.3.acceptance.json +160 -0
- package/docs/releases/v0.0.3.md +62 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,36 @@
|
|
|
3
3
|
本项目遵循 Keep a Changelog 的结构;`0.0.x` 为技术预览。
|
|
4
4
|
This project follows the Keep a Changelog structure; `0.0.x` is a technical preview.
|
|
5
5
|
|
|
6
|
+
## [0.0.3] - 2026-08-29
|
|
7
|
+
|
|
8
|
+
### 中文
|
|
9
|
+
|
|
10
|
+
#### 新增
|
|
11
|
+
|
|
12
|
+
- OpenAI Codex 设置入口改用独立的代码括号图标,与其他 Provider 插件保持可辨识的视觉差异。
|
|
13
|
+
- 额度卡新增带图标的刷新按钮、刷新中状态、响应式双列布局和更清晰的剩余额度层级;五小时额度始终显示精确重置时间,每周额度在不足 24 小时时显示精确时间,其余时间显示相对天数,完整时间保留在提示与无障碍标签中。
|
|
14
|
+
- 模型设置按“已选择在前、未选择在后”排序;未选择模型默认收起并可按需展开,同时显示已选择、当前显示与总数摘要。
|
|
15
|
+
|
|
16
|
+
#### 修复
|
|
17
|
+
|
|
18
|
+
- 缩短设置页内容高度并避免插件覆盖 Harness 弹窗宽度或滚动容器,改善窄屏与较矮窗口中的滚动体验。
|
|
19
|
+
- 模型能力改为与 Grok Build 一致的紧凑标签,使用 `272K`、`128K` 一类上下文与最大输出表示,不推测 provider catalog 未提供的能力。
|
|
20
|
+
- 在收起未选择模型时取消勾选,键盘焦点会回到展开按钮,避免焦点停留在隐藏元素;重置时间解析也会拒绝无法构造有效日期的值。
|
|
21
|
+
|
|
22
|
+
### English
|
|
23
|
+
|
|
24
|
+
#### Added
|
|
25
|
+
|
|
26
|
+
- The OpenAI Codex settings entry now uses a dedicated code-brackets icon that remains visually distinct from other provider plugins.
|
|
27
|
+
- Usage cards now provide an icon button with a refreshing state, a responsive two-column layout, and a clearer remaining-usage hierarchy. Five-hour limits always show the exact reset time; weekly limits show the exact time within 24 hours and otherwise use relative days, while the complete timestamp remains available in tooltips and accessible labels.
|
|
28
|
+
- Model settings now sort selected models before unselected models. Unselected models are collapsed by default and can be expanded on demand, with selected, visible, and total counts shown in the summary.
|
|
29
|
+
|
|
30
|
+
#### Fixed
|
|
31
|
+
|
|
32
|
+
- The settings content is shorter and no longer overrides the Harness dialog width or scroll container, improving scrolling in narrow or low-height windows.
|
|
33
|
+
- Model capabilities use compact Grok Build-style labels such as `272K` and `128K` for context and maximum output, without inferring capabilities absent from the provider catalog.
|
|
34
|
+
- Deselecting a model while unselected entries are collapsed returns keyboard focus to the expansion control instead of leaving it on a hidden element; reset-time parsing also rejects values that cannot produce a valid date.
|
|
35
|
+
|
|
6
36
|
## [0.0.2] - 2026-08-29
|
|
7
37
|
|
|
8
38
|
### 中文
|
package/README.en.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
A Codex plugin for DeepSeek Harness with ChatGPT OAuth sign-in, Codex models, image input, and reliable streaming responses.
|
|
6
6
|
|
|
7
|
-
> Current version: `0.0.
|
|
7
|
+
> Current version: `0.0.3` technical preview, published under npm `latest` with a full GitHub Release. npm package: `dsh-codex-community`.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -16,6 +16,8 @@ A Codex plugin for DeepSeek Harness with ChatGPT OAuth sign-in, Codex models, im
|
|
|
16
16
|
- classify `AccountQuotaExceeded` as non-retryable account quota and show a sanitized reset time;
|
|
17
17
|
- preserve safe partial text after a stream failure, while incomplete tool calls stop without replaying the request;
|
|
18
18
|
- read the real five-hour and weekly usage limits whenever the settings page opens or is refreshed manually, with a safe fallback and no OAuth credentials exposed to the Web page;
|
|
19
|
+
- present remaining usage in compact cards: five-hour limits show the exact reset time, while weekly limits show the exact time within 24 hours and otherwise use relative days;
|
|
20
|
+
- list selected models first in settings and collapse unselected models by default, with compact `K` labels taken from the current provider catalog for context and maximum output;
|
|
19
21
|
- use the lightning button to the left of the model selector or `/codex` to choose official Fast (1.5×) and transport for the current session, with Fast off and transport automatic by default.
|
|
20
22
|
|
|
21
23
|
## Requirements
|
|
@@ -28,7 +30,7 @@ A Codex plugin for DeepSeek Harness with ChatGPT OAuth sign-in, Codex models, im
|
|
|
28
30
|
Install an exact version:
|
|
29
31
|
|
|
30
32
|
```sh
|
|
31
|
-
dsh plugin --profile web add dsh-codex-community@0.0.
|
|
33
|
+
dsh plugin --profile web add dsh-codex-community@0.0.3
|
|
32
34
|
dsh web
|
|
33
35
|
```
|
|
34
36
|
|
|
@@ -56,7 +58,7 @@ Fast and transport preferences are kept only for the current session in the curr
|
|
|
56
58
|
Update:
|
|
57
59
|
|
|
58
60
|
```sh
|
|
59
|
-
dsh plugin --profile web update dsh-codex-community@0.0.
|
|
61
|
+
dsh plugin --profile web update dsh-codex-community@0.0.3
|
|
60
62
|
dsh web
|
|
61
63
|
```
|
|
62
64
|
|
|
@@ -79,8 +81,8 @@ See [Troubleshooting](docs/troubleshooting.en.md) for diagnostic steps.
|
|
|
79
81
|
## Support boundaries
|
|
80
82
|
|
|
81
83
|
- The `0.0.x` line does not promise a stable API; pin the exact version.
|
|
82
|
-
- Linux, macOS, and Windows CI/profile smoke for `0.0.
|
|
83
|
-
- Live OAuth, conversation, image, transport, and Fast network acceptance
|
|
84
|
+
- Linux, macOS, and Windows CI/profile smoke for `0.0.3` reached `3/3`; the accepted commit and platform evidence are in the [acceptance record](docs/releases/v0.0.3.acceptance.json).
|
|
85
|
+
- Live OAuth, conversation, image, transport, and Fast network acceptance for `0.0.3` starts in a fresh record at `0/13 pending`. These unverified capabilities remain clearly disclosed and will be completed individually after publication.
|
|
84
86
|
- The settings page reads usage through the Web-backend compatibility endpoint used by the official Codex client. If that endpoint is unavailable or malformed, the page retains the latest safe reading and falls back to request observation or an unknown state instead of presenting the error as zero remaining usage.
|
|
85
87
|
- Model names, context windows, and maximum outputs come from the installed provider catalog. The reasoning picker exposes only subscription-Codex levels through Max that the Provider can fully implement, plus each model's verified default. Generic `Default`, `Off`, and `Minimal` entries are hidden, and agent-level `Ultra` is not disguised as a plain reasoning effort.
|
|
86
88
|
- Fast is limited to GPT-5.4, GPT-5.5, GPT-5.6 Luna, Sol, and Terra and consumes more usage. Other models never receive the Fast service tier.
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
面向 DeepSeek Harness 的 Codex 插件,提供 ChatGPT OAuth 登录、Codex 模型、图片输入和可靠的流式回复。
|
|
6
6
|
|
|
7
|
-
> 当前版本:`0.0.
|
|
7
|
+
> 当前版本:`0.0.3` 技术预览,使用 npm `latest` 与正式 GitHub Release 发布。npm 包名:`dsh-codex-community`。
|
|
8
8
|
|
|
9
9
|
## 功能
|
|
10
10
|
|
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
- 将 `AccountQuotaExceeded` 识别为不可重试的账户配额错误,并显示脱敏后的重置时间;
|
|
17
17
|
- 流中断时保留安全的半段纯文本;涉及未完成工具调用时停止,不自动重放请求;
|
|
18
18
|
- 每次进入设置页或手动刷新时读取真实的 5 小时与每周额度;读取失败时安全降级,不向 Web 页面暴露 OAuth 凭据;
|
|
19
|
+
- 使用紧凑的额度卡展示剩余比例:5 小时额度显示精确重置时间,每周额度在不足 24 小时时显示精确时间,其余时间显示相对天数;
|
|
20
|
+
- 在设置页优先展示已选择模型,默认收起未选择模型;上下文和最大输出使用来自当前 provider catalog 的紧凑 `K` 标签;
|
|
19
21
|
- 使用模型选择器左侧的闪电按钮或 `/codex` 为当前会话切换官方 Fast(1.5 倍速)和传输方式,默认关闭 Fast、自动选择传输。
|
|
20
22
|
|
|
21
23
|
## 运行要求
|
|
@@ -28,7 +30,7 @@
|
|
|
28
30
|
请固定安装精确版本:
|
|
29
31
|
|
|
30
32
|
```sh
|
|
31
|
-
dsh plugin --profile web add dsh-codex-community@0.0.
|
|
33
|
+
dsh plugin --profile web add dsh-codex-community@0.0.3
|
|
32
34
|
dsh web
|
|
33
35
|
```
|
|
34
36
|
|
|
@@ -56,7 +58,7 @@ Fast 和传输偏好只保存在当前进程的当前会话中,不会持久化
|
|
|
56
58
|
更新:
|
|
57
59
|
|
|
58
60
|
```sh
|
|
59
|
-
dsh plugin --profile web update dsh-codex-community@0.0.
|
|
61
|
+
dsh plugin --profile web update dsh-codex-community@0.0.3
|
|
60
62
|
dsh web
|
|
61
63
|
```
|
|
62
64
|
|
|
@@ -79,8 +81,8 @@ dsh web
|
|
|
79
81
|
## 支持边界
|
|
80
82
|
|
|
81
83
|
- `0.0.x` 不承诺稳定 API,请固定版本;
|
|
82
|
-
- `0.0.
|
|
83
|
-
-
|
|
84
|
+
- `0.0.3` 的 Linux、macOS 和 Windows CI/profile smoke 已达到 `3/3`,候选提交与平台证据见[验收记录](docs/releases/v0.0.3.acceptance.json);
|
|
85
|
+
- `0.0.3` 的真实 OAuth、对话、图片、transport 和 Fast 网络验收从独立记录的 `0/13 pending` 开始;这些未验证能力继续如实展示,并在发布后逐项补齐;
|
|
84
86
|
- 设置页额度通过官方 Codex 客户端使用的 Web 后端兼容接口读取;接口不可用或返回异常时保留最近的安全读数,并降级为请求观测或未知状态,不把错误伪装成零余额;
|
|
85
87
|
- 模型名称、上下文和最大输出来自当前安装的 provider catalog;推理选择器只展示已按订阅 Codex 目录核验且 Provider 能完整实现的 Low 至 Max 与模型默认值,不展示通用的 `Default`、`Off`、`Minimal`,也不把需要主动任务委派的 `Ultra` 伪装成普通推理档位;
|
|
86
88
|
- Fast 仅用于 GPT-5.4、GPT-5.5、GPT-5.6 Luna、Sol 和 Terra,并会提高额度消耗;其他模型不会发送 Fast service tier;
|
package/dist/client/index.js
CHANGED
|
@@ -28,6 +28,7 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
unavailable: "当前环境没有可用的 Codex 登录流程。",
|
|
29
29
|
writableNo: "当前凭据存储不可写。",
|
|
30
30
|
refresh: "刷新",
|
|
31
|
+
refreshing: "刷新中…",
|
|
31
32
|
signIn: "登录",
|
|
32
33
|
signInWithChatGPT: "使用 ChatGPT 登录",
|
|
33
34
|
signInAgain: "重新登录",
|
|
@@ -60,19 +61,22 @@ window.__ModuleLoader__.load({
|
|
|
60
61
|
quotaWeekly: "每周额度",
|
|
61
62
|
quotaMinuteWindow: "分钟额度",
|
|
62
63
|
quotaRemaining: "剩余",
|
|
63
|
-
quotaUsed: "已使用",
|
|
64
64
|
quotaResetsAt: "重置时间",
|
|
65
65
|
quotaUpdatedAt: "数据更新时间",
|
|
66
|
+
quotaResetSoon: "即将重置",
|
|
67
|
+
quotaResetPrefix: "",
|
|
68
|
+
quotaResetSuffix: "后重置",
|
|
69
|
+
quotaResetMinute: "分钟",
|
|
70
|
+
quotaResetHour: "小时",
|
|
71
|
+
quotaResetDay: "天",
|
|
66
72
|
quotaRecentSuccess: "最近一次 Codex 请求成功",
|
|
67
73
|
quotaSuccessCaution: "这只表示最近一次请求成功,不代表账户剩余额度。",
|
|
68
74
|
quotaExhausted: "最近观测到 Codex 账户额度已用尽。",
|
|
69
75
|
quotaObservedAt: "观测时间",
|
|
70
76
|
quotaResetAt: "观测到的重置时间",
|
|
71
|
-
quotaResetIn: "预计还有约",
|
|
72
|
-
quotaMinutes: "分钟",
|
|
73
77
|
quotaNoReset: "未获得通过校验的重置时间;状态会在有限时间后自动回到未知。",
|
|
74
78
|
modelsTitle: "当前安装的 Codex 模型",
|
|
75
|
-
modelsDescription: "
|
|
79
|
+
modelsDescription: "选择在模型选择器中显示的 Codex 模型。名称、上下文和最大输出来自当前安装的 provider catalog;隐藏模型不影响已有会话。",
|
|
76
80
|
modelsContextWindow: "上下文",
|
|
77
81
|
modelsMaxOutput: "最大输出",
|
|
78
82
|
modelsLoading: "正在读取 Codex 模型目录…",
|
|
@@ -83,8 +87,11 @@ window.__ModuleLoader__.load({
|
|
|
83
87
|
modelsSaving: "正在保存…",
|
|
84
88
|
modelsSaved: "模型启用设置已保存。",
|
|
85
89
|
modelsAtLeastOne: "至少需要启用一个 Codex 模型。",
|
|
86
|
-
modelsAllEnabledFollow: "
|
|
87
|
-
modelsAllEnabledPreserve: "
|
|
90
|
+
modelsAllEnabledFollow: "已启用全部当前目录模型;保存后将清除列表覆盖,未来新增模型会自动显示。",
|
|
91
|
+
modelsAllEnabledPreserve: "已启用全部当前目录模型;为保留其他模型参数,保存后会固定当前列表,未来新增模型不会自动显示。",
|
|
92
|
+
modelsShowMore: "显示未选择的 {count} 个模型",
|
|
93
|
+
modelsCollapse: "收起未选择的模型",
|
|
94
|
+
modelsSummary: "已选择 {selected}/{total} · 当前显示 {visible}/{total}",
|
|
88
95
|
modelsRetry: "重新读取",
|
|
89
96
|
modelsLoadFailed: "读取 Codex 模型目录失败。",
|
|
90
97
|
modelsSaveFailed: "保存模型启用设置失败。",
|
|
@@ -112,6 +119,7 @@ window.__ModuleLoader__.load({
|
|
|
112
119
|
unavailable: "No Codex sign-in flow is available in this environment.",
|
|
113
120
|
writableNo: "The credential store is currently read-only.",
|
|
114
121
|
refresh: "Refresh",
|
|
122
|
+
refreshing: "Refreshing…",
|
|
115
123
|
signIn: "Sign in",
|
|
116
124
|
signInWithChatGPT: "Sign in with ChatGPT",
|
|
117
125
|
signInAgain: "Sign in again",
|
|
@@ -144,19 +152,22 @@ window.__ModuleLoader__.load({
|
|
|
144
152
|
quotaWeekly: "Weekly limit",
|
|
145
153
|
quotaMinuteWindow: "minute limit",
|
|
146
154
|
quotaRemaining: "Remaining",
|
|
147
|
-
quotaUsed: "Used",
|
|
148
155
|
quotaResetsAt: "Resets",
|
|
149
156
|
quotaUpdatedAt: "Data updated",
|
|
157
|
+
quotaResetSoon: "Resets soon",
|
|
158
|
+
quotaResetPrefix: "Resets in",
|
|
159
|
+
quotaResetSuffix: "",
|
|
160
|
+
quotaResetMinute: "minute",
|
|
161
|
+
quotaResetHour: "hour",
|
|
162
|
+
quotaResetDay: "day",
|
|
150
163
|
quotaRecentSuccess: "The latest Codex request succeeded",
|
|
151
164
|
quotaSuccessCaution: "This only describes the latest request; it does not represent remaining account quota.",
|
|
152
165
|
quotaExhausted: "Codex account quota was exhausted in the latest observation.",
|
|
153
166
|
quotaObservedAt: "Observed at",
|
|
154
167
|
quotaResetAt: "Observed reset time",
|
|
155
|
-
quotaResetIn: "Approximately",
|
|
156
|
-
quotaMinutes: "minutes remaining",
|
|
157
168
|
quotaNoReset: "No reset time passed validation; this state automatically returns to unknown after a bounded interval.",
|
|
158
169
|
modelsTitle: "Codex models in this installation",
|
|
159
|
-
modelsDescription: "Choose which Codex models appear in the model selector.
|
|
170
|
+
modelsDescription: "Choose which Codex models appear in the model selector. Names, context windows, and maximum output values come from the installed provider catalog; hidden models do not affect existing conversations.",
|
|
160
171
|
modelsContextWindow: "Context",
|
|
161
172
|
modelsMaxOutput: "Max output",
|
|
162
173
|
modelsLoading: "Reading the Codex model catalog…",
|
|
@@ -167,8 +178,11 @@ window.__ModuleLoader__.load({
|
|
|
167
178
|
modelsSaving: "Saving…",
|
|
168
179
|
modelsSaved: "Enabled models saved.",
|
|
169
180
|
modelsAtLeastOne: "At least one Codex model must remain enabled.",
|
|
170
|
-
modelsAllEnabledFollow: "All current catalog models are enabled.
|
|
171
|
-
modelsAllEnabledPreserve: "All current catalog models are enabled. To preserve
|
|
181
|
+
modelsAllEnabledFollow: "All current catalog models are enabled. Saving clears the list override, so future models appear automatically.",
|
|
182
|
+
modelsAllEnabledPreserve: "All current catalog models are enabled. To preserve other model settings, saving fixes the current list and future models will not appear automatically.",
|
|
183
|
+
modelsShowMore: "Show {count} unselected models",
|
|
184
|
+
modelsCollapse: "Hide unselected models",
|
|
185
|
+
modelsSummary: "Selected {selected}/{total} · Showing {visible}/{total}",
|
|
172
186
|
modelsRetry: "Reload",
|
|
173
187
|
modelsLoadFailed: "Could not read the Codex model catalog.",
|
|
174
188
|
modelsSaveFailed: "Could not save the enabled models.",
|
|
@@ -184,6 +198,23 @@ window.__ModuleLoader__.load({
|
|
|
184
198
|
legacyReasoningRepairHelp: "This conversation saved an unsupported Off/Minimal level. Click to switch to this model's current default. Like using the model selector, this also saves it as the default model for future conversations.",
|
|
185
199
|
}
|
|
186
200
|
|
|
201
|
+
const NAV_ICON_MARKER = "data-dsh-codex-community-nav-icon"
|
|
202
|
+
const NAV_ICON_STYLE_SELECTOR = 'style[data-plugin-nav-icon="dsh-codex-community"]'
|
|
203
|
+
const NAV_ICON_STATE_KEY = Symbol.for("dsh-codex-community.settings-nav-icon.v1")
|
|
204
|
+
const NAV_ICON_LABELS = new Set([zh.nav.trim(), en.nav.trim()])
|
|
205
|
+
const SETTINGS_DIALOG_SELECTOR = '[role="dialog"][aria-modal="true"]'
|
|
206
|
+
const SETTINGS_NAV_SELECTOR = `${SETTINGS_DIALOG_SELECTOR} > nav`
|
|
207
|
+
const SETTINGS_NAV_BUTTON_SELECTOR = `${SETTINGS_NAV_SELECTOR} button`
|
|
208
|
+
// settings.section has no icon slot in Harness 0.1.1-rc.2. This original code-brackets glyph is applied only to the unique Codex nav entry.
|
|
209
|
+
const CODE_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
210
|
+
<path d="M5.25 3.25 1.5 8l3.75 4.75M10.75 3.25 14.5 8l-3.75 4.75M9.25 2.5l-2.5 11" fill="none" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
211
|
+
</svg>`
|
|
212
|
+
const CODE_ICON_MASK = `data:image/svg+xml,${encodeURIComponent(CODE_ICON_SVG).replace(/[!'()*]/gu, (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`)}`
|
|
213
|
+
const NAV_ICON_CSS = [
|
|
214
|
+
`[${NAV_ICON_MARKER}]>svg:first-child{display:none!important}`,
|
|
215
|
+
`[${NAV_ICON_MARKER}]::before{content:"";width:16px;height:16px;flex:none;background:currentColor;-webkit-mask:url("${CODE_ICON_MASK}") center/contain no-repeat;mask:url("${CODE_ICON_MASK}") center/contain no-repeat}`,
|
|
216
|
+
].join("")
|
|
217
|
+
|
|
187
218
|
function createAuthorizationClient(connection) {
|
|
188
219
|
const call = async (endpoint, payload, signal) => {
|
|
189
220
|
const result = await connection.rpc.call(CHANNEL, endpoint, payload, signal)
|
|
@@ -421,7 +452,9 @@ window.__ModuleLoader__.load({
|
|
|
421
452
|
return { status: "unknown" }
|
|
422
453
|
}
|
|
423
454
|
if (value.resetAt === undefined) return { status: value.status, observedAt: value.observedAt }
|
|
424
|
-
if (!Number.isSafeInteger(value.resetAt) || value.resetAt <= now
|
|
455
|
+
if (!Number.isSafeInteger(value.resetAt) || value.resetAt <= now || !Number.isFinite(new Date(value.resetAt).getTime())) {
|
|
456
|
+
return { status: "unknown" }
|
|
457
|
+
}
|
|
425
458
|
return {
|
|
426
459
|
status: value.status,
|
|
427
460
|
observedAt: value.observedAt,
|
|
@@ -458,7 +491,7 @@ window.__ModuleLoader__.load({
|
|
|
458
491
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined
|
|
459
492
|
if (typeof value.usedPercent !== "number" || !Number.isFinite(value.usedPercent) || value.usedPercent < 0 || value.usedPercent > 100) return undefined
|
|
460
493
|
if (!Number.isSafeInteger(value.windowDurationMins) || value.windowDurationMins < 1 || value.windowDurationMins > 525_600) return undefined
|
|
461
|
-
if (!Number.isSafeInteger(value.resetsAt) || value.resetsAt < 0) return undefined
|
|
494
|
+
if (!Number.isSafeInteger(value.resetsAt) || value.resetsAt < 0 || !Number.isFinite(new Date(value.resetsAt).getTime())) return undefined
|
|
462
495
|
return {
|
|
463
496
|
usedPercent: value.usedPercent,
|
|
464
497
|
windowDurationMins: value.windowDurationMins,
|
|
@@ -476,6 +509,65 @@ window.__ModuleLoader__.load({
|
|
|
476
509
|
return `${window.windowDurationMins} ${t("quotaMinuteWindow")}`
|
|
477
510
|
}
|
|
478
511
|
|
|
512
|
+
function formatResetDistance(resetsAt, t, now = Date.now()) {
|
|
513
|
+
if (!Number.isSafeInteger(resetsAt) || !Number.isSafeInteger(now) || resetsAt < 0 || now < 0) {
|
|
514
|
+
return t("quotaResetSoon")
|
|
515
|
+
}
|
|
516
|
+
const remaining = resetsAt - now
|
|
517
|
+
if (remaining <= 60_000) return t("quotaResetSoon")
|
|
518
|
+
let count
|
|
519
|
+
let unit
|
|
520
|
+
if (remaining < 60 * 60_000) {
|
|
521
|
+
count = Math.ceil(remaining / 60_000)
|
|
522
|
+
unit = t("quotaResetMinute")
|
|
523
|
+
} else if (remaining < 24 * 60 * 60_000) {
|
|
524
|
+
count = Math.ceil(remaining / (60 * 60_000))
|
|
525
|
+
unit = t("quotaResetHour")
|
|
526
|
+
} else {
|
|
527
|
+
count = Math.ceil(remaining / (24 * 60 * 60_000))
|
|
528
|
+
unit = t("quotaResetDay")
|
|
529
|
+
}
|
|
530
|
+
if (t("locale").toLowerCase().startsWith("zh")) {
|
|
531
|
+
return `${count} ${unit}${t("quotaResetSuffix")}`
|
|
532
|
+
}
|
|
533
|
+
return `${t("quotaResetPrefix")} ${count} ${unit}${count === 1 ? "" : "s"}`
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function renderResetTime(resetsAt, t, { showExact = false, now = Date.now() } = {}) {
|
|
537
|
+
const exact = new Date(resetsAt).toLocaleString(t("locale"))
|
|
538
|
+
const relative = formatResetDistance(resetsAt, t, now)
|
|
539
|
+
const separator = t("locale").toLowerCase().startsWith("zh") ? ":" : ": "
|
|
540
|
+
const exactLabel = `${t("quotaResetsAt")}${separator}${exact}`
|
|
541
|
+
return h("time", {
|
|
542
|
+
dateTime: new Date(resetsAt).toISOString(),
|
|
543
|
+
title: exactLabel,
|
|
544
|
+
"aria-label": showExact ? exactLabel : `${relative} · ${exactLabel}`,
|
|
545
|
+
}, showExact ? exactLabel : relative)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function refreshIcon() {
|
|
549
|
+
return h("svg", {
|
|
550
|
+
className: "dshCodexRefreshIcon",
|
|
551
|
+
viewBox: "0 0 16 16",
|
|
552
|
+
fill: "none",
|
|
553
|
+
"aria-hidden": "true",
|
|
554
|
+
focusable: "false",
|
|
555
|
+
},
|
|
556
|
+
h("path", {
|
|
557
|
+
d: "M13.25 5.75A5.5 5.5 0 1 0 13.1 10.6",
|
|
558
|
+
stroke: "currentColor",
|
|
559
|
+
strokeWidth: "1.4",
|
|
560
|
+
strokeLinecap: "round",
|
|
561
|
+
}),
|
|
562
|
+
h("path", {
|
|
563
|
+
d: "M10.25 5.75h3v-3",
|
|
564
|
+
stroke: "currentColor",
|
|
565
|
+
strokeWidth: "1.4",
|
|
566
|
+
strokeLinecap: "round",
|
|
567
|
+
strokeLinejoin: "round",
|
|
568
|
+
}))
|
|
569
|
+
}
|
|
570
|
+
|
|
479
571
|
function renderObservedQuota(quota, t) {
|
|
480
572
|
if (quota.status === "recent-success") {
|
|
481
573
|
return h(React.Fragment, null,
|
|
@@ -488,7 +580,9 @@ window.__ModuleLoader__.load({
|
|
|
488
580
|
h("p", { className: "dshCodexMuted" }, `${t("quotaObservedAt")} · ${new Date(quota.observedAt).toLocaleString(t("locale"))}`),
|
|
489
581
|
quota.resetAt === undefined
|
|
490
582
|
? h("p", { className: "dshCodexMuted" }, t("quotaNoReset"))
|
|
491
|
-
: h("p", { className: "dshCodexMuted" },
|
|
583
|
+
: h("p", { className: "dshCodexMuted" },
|
|
584
|
+
`${t("quotaResetAt")} · `,
|
|
585
|
+
renderResetTime(quota.resetAt, t)))
|
|
492
586
|
}
|
|
493
587
|
return h(React.Fragment, null,
|
|
494
588
|
h("p", { className: "dshCodexQuotaState dshCodexQuotaUnknown" }, t("quotaUnknown")),
|
|
@@ -496,6 +590,7 @@ window.__ModuleLoader__.load({
|
|
|
496
590
|
}
|
|
497
591
|
|
|
498
592
|
function renderAccountUsage(usage, t) {
|
|
593
|
+
const now = Date.now()
|
|
499
594
|
return h(React.Fragment, null,
|
|
500
595
|
...usage.rateLimits.map((limit) => {
|
|
501
596
|
const windows = [limit.primary, limit.secondary].filter((window) => window !== undefined)
|
|
@@ -504,26 +599,30 @@ window.__ModuleLoader__.load({
|
|
|
504
599
|
: limit.limitName ?? limit.limitId
|
|
505
600
|
return h("section", { key: limit.limitId, className: "dshCodexQuotaGroup" },
|
|
506
601
|
h("p", { className: "dshCodexQuotaProduct" }, limitLabel),
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
602
|
+
h("div", { className: "dshCodexQuotaWindows" },
|
|
603
|
+
...windows.map((window) => {
|
|
604
|
+
const remaining = Math.max(0, Math.min(100, 100 - window.usedPercent))
|
|
605
|
+
const remainingText = percentText(remaining)
|
|
606
|
+
const label = usageWindowLabel(window, t)
|
|
607
|
+
const resetDistance = window.resetsAt - now
|
|
608
|
+
const showExactReset = window.windowDurationMins === 300
|
|
609
|
+
|| (window.windowDurationMins === 10_080
|
|
610
|
+
&& resetDistance > 0
|
|
611
|
+
&& resetDistance < 24 * 60 * 60_000)
|
|
612
|
+
return h("div", { key: `${limit.limitId}-${window.windowDurationMins}`, className: "dshCodexQuotaWindow" },
|
|
613
|
+
h("div", { className: "dshCodexQuotaWindowHeader" },
|
|
614
|
+
h("span", null, label),
|
|
615
|
+
h("strong", null, `${t("quotaRemaining")} ${remainingText}%`)),
|
|
616
|
+
h("progress", {
|
|
617
|
+
max: 100,
|
|
618
|
+
value: remaining,
|
|
619
|
+
"aria-label": `${limitLabel} · ${label} · ${t("quotaRemaining")} ${remainingText}%`,
|
|
620
|
+
}),
|
|
621
|
+
h("div", { className: "dshCodexQuotaMeta" },
|
|
622
|
+
renderResetTime(window.resetsAt, t, { showExact: showExactReset, now })))
|
|
623
|
+
})))
|
|
525
624
|
}),
|
|
526
|
-
h("p", { className: "dshCodexMuted dshCodexQuotaUpdated" }, `${t("quotaUpdatedAt")}
|
|
625
|
+
h("p", { className: "dshCodexMuted dshCodexQuotaUpdated" }, `${t("quotaUpdatedAt")}${t("locale").toLowerCase().startsWith("zh") ? ":" : ": "}${new Date(usage.observedAt).toLocaleString(t("locale"))}`))
|
|
527
626
|
}
|
|
528
627
|
|
|
529
628
|
function objectAtPath(root, path) {
|
|
@@ -1103,6 +1202,7 @@ window.__ModuleLoader__.load({
|
|
|
1103
1202
|
h("section", {
|
|
1104
1203
|
className: "dshCodexQuota",
|
|
1105
1204
|
"data-quota-status": quota.status,
|
|
1205
|
+
"aria-busy": usageSnapshot.status === "loading" ? "true" : undefined,
|
|
1106
1206
|
"aria-live": "polite",
|
|
1107
1207
|
"aria-labelledby": "dsh-codex-quota-title",
|
|
1108
1208
|
},
|
|
@@ -1111,9 +1211,13 @@ window.__ModuleLoader__.load({
|
|
|
1111
1211
|
h("button", {
|
|
1112
1212
|
type: "button",
|
|
1113
1213
|
className: "dshCodexButton dshCodexRefreshButton",
|
|
1214
|
+
"data-loading": usageSnapshot.status === "loading" ? "true" : undefined,
|
|
1215
|
+
"aria-busy": usageSnapshot.status === "loading" ? "true" : undefined,
|
|
1114
1216
|
disabled: actionBusy || usageSnapshot.status === "loading",
|
|
1115
1217
|
onClick: refresh,
|
|
1116
|
-
},
|
|
1218
|
+
},
|
|
1219
|
+
refreshIcon(),
|
|
1220
|
+
h("span", null, usageSnapshot.status === "loading" ? t("refreshing") : t("refresh")))),
|
|
1117
1221
|
liveUsage === undefined
|
|
1118
1222
|
? h("p", { className: "dshCodexQuotaProduct" }, t("quotaProduct"))
|
|
1119
1223
|
: null,
|
|
@@ -1133,13 +1237,17 @@ window.__ModuleLoader__.load({
|
|
|
1133
1237
|
const [saving, setSaving] = React.useState(false)
|
|
1134
1238
|
const [saved, setSaved] = React.useState(false)
|
|
1135
1239
|
const [failure, setFailure] = React.useState(null)
|
|
1240
|
+
const [expanded, setExpanded] = React.useState(false)
|
|
1136
1241
|
const generationRef = React.useRef(0)
|
|
1137
1242
|
const mountedRef = React.useRef(false)
|
|
1243
|
+
const modelToggleRef = React.useRef(null)
|
|
1244
|
+
const focusModelToggleRef = React.useRef(false)
|
|
1138
1245
|
|
|
1139
1246
|
const refresh = React.useCallback(async () => {
|
|
1140
1247
|
const generation = ++generationRef.current
|
|
1141
1248
|
setSaved(false)
|
|
1142
1249
|
setFailure(null)
|
|
1250
|
+
setExpanded(false)
|
|
1143
1251
|
setSnapshot({ kind: "loading" })
|
|
1144
1252
|
try {
|
|
1145
1253
|
const next = await client.load()
|
|
@@ -1164,6 +1272,12 @@ window.__ModuleLoader__.load({
|
|
|
1164
1272
|
}
|
|
1165
1273
|
}, [client, refresh])
|
|
1166
1274
|
|
|
1275
|
+
React.useEffect(() => {
|
|
1276
|
+
if (!focusModelToggleRef.current) return
|
|
1277
|
+
focusModelToggleRef.current = false
|
|
1278
|
+
if (typeof modelToggleRef.current?.focus === "function") modelToggleRef.current.focus()
|
|
1279
|
+
}, [expanded, selectedIds])
|
|
1280
|
+
|
|
1167
1281
|
const toggle = (id) => {
|
|
1168
1282
|
const selected = selectedIds.includes(id)
|
|
1169
1283
|
if (selected && selectedIds.length === 1) {
|
|
@@ -1172,6 +1286,7 @@ window.__ModuleLoader__.load({
|
|
|
1172
1286
|
}
|
|
1173
1287
|
setSaved(false)
|
|
1174
1288
|
setFailure(null)
|
|
1289
|
+
if (selected && !expanded) focusModelToggleRef.current = true
|
|
1175
1290
|
setSelectedIds(selected
|
|
1176
1291
|
? selectedIds.filter((candidate) => candidate !== id)
|
|
1177
1292
|
: [...selectedIds, id])
|
|
@@ -1204,6 +1319,7 @@ window.__ModuleLoader__.load({
|
|
|
1204
1319
|
}
|
|
1205
1320
|
|
|
1206
1321
|
let content
|
|
1322
|
+
let modelSummary = null
|
|
1207
1323
|
if (snapshot.kind === "loading") {
|
|
1208
1324
|
content = h("p", { className: "dshCodexMuted", "aria-busy": "true" }, t("modelsLoading"))
|
|
1209
1325
|
} else if (snapshot.kind === "unavailable") {
|
|
@@ -1223,7 +1339,17 @@ window.__ModuleLoader__.load({
|
|
|
1223
1339
|
const original = new Set(snapshot.selectedIds)
|
|
1224
1340
|
const dirty = selected.size !== original.size || [...selected].some((id) => !original.has(id))
|
|
1225
1341
|
const plan = modelSavePlan(snapshot, selectedIds)
|
|
1226
|
-
const
|
|
1342
|
+
const selectedModels = snapshot.models.filter((model) => selected.has(model.id))
|
|
1343
|
+
const unselectedModels = snapshot.models.filter((model) => !selected.has(model.id))
|
|
1344
|
+
const hiddenModelCount = unselectedModels.length
|
|
1345
|
+
const visibleModels = expanded
|
|
1346
|
+
? [...selectedModels, ...unselectedModels]
|
|
1347
|
+
: selectedModels
|
|
1348
|
+
modelSummary = t("modelsSummary")
|
|
1349
|
+
.replace("{selected}", String(selected.size))
|
|
1350
|
+
.replaceAll("{total}", String(snapshot.models.length))
|
|
1351
|
+
.replace("{visible}", String(visibleModels.length))
|
|
1352
|
+
const modelItems = visibleModels.map((model) => {
|
|
1227
1353
|
const metadata = [
|
|
1228
1354
|
...(model.contextWindow === undefined
|
|
1229
1355
|
? []
|
|
@@ -1260,9 +1386,21 @@ window.__ModuleLoader__.load({
|
|
|
1260
1386
|
content = h(React.Fragment, null,
|
|
1261
1387
|
snapshot.writable ? null : h("p", { className: "dshCodexMuted" }, t("modelsReadOnly")),
|
|
1262
1388
|
h("ul", {
|
|
1389
|
+
id: "dsh-codex-model-list",
|
|
1263
1390
|
className: "dshCodexModelList",
|
|
1264
1391
|
"aria-labelledby": "dsh-codex-models-title",
|
|
1265
1392
|
}, ...modelItems),
|
|
1393
|
+
hiddenModelCount === 0 ? null : h("button", {
|
|
1394
|
+
type: "button",
|
|
1395
|
+
className: "dshCodexButton dshCodexModelToggle",
|
|
1396
|
+
"aria-controls": "dsh-codex-model-list",
|
|
1397
|
+
"aria-expanded": expanded ? "true" : "false",
|
|
1398
|
+
ref: modelToggleRef,
|
|
1399
|
+
disabled: saving,
|
|
1400
|
+
onClick: () => setExpanded((current) => !current),
|
|
1401
|
+
}, expanded
|
|
1402
|
+
? t("modelsCollapse")
|
|
1403
|
+
: t("modelsShowMore").replace("{count}", String(hiddenModelCount))),
|
|
1266
1404
|
plan.allCatalogSelected ? h("p", { className: "dshCodexMuted" }, t(plan.unsetOverride
|
|
1267
1405
|
? "modelsAllEnabledFollow"
|
|
1268
1406
|
: "modelsAllEnabledPreserve")) : null,
|
|
@@ -1286,7 +1424,11 @@ window.__ModuleLoader__.load({
|
|
|
1286
1424
|
return h("section", { className: "dshCodexPanel dshCodexCard dshCodexModelPanel" },
|
|
1287
1425
|
h("header", { className: "dshCodexHeader" },
|
|
1288
1426
|
h("div", null,
|
|
1289
|
-
h("
|
|
1427
|
+
h("div", { className: "dshCodexModelTitleRow" },
|
|
1428
|
+
h("h3", { id: "dsh-codex-models-title" }, t("modelsTitle")),
|
|
1429
|
+
modelSummary === null
|
|
1430
|
+
? null
|
|
1431
|
+
: h("span", { className: "dshCodexModelSummary" }, modelSummary)),
|
|
1290
1432
|
h("p", { className: "dshCodexWarning" }, t("modelsDescription")))),
|
|
1291
1433
|
content)
|
|
1292
1434
|
}
|
|
@@ -1304,6 +1446,7 @@ window.__ModuleLoader__.load({
|
|
|
1304
1446
|
|
|
1305
1447
|
function apply(ctx) {
|
|
1306
1448
|
ctx.effect(installStyle, "dsh-codex: client styles")
|
|
1449
|
+
ctx.effect(installSettingsNavIcon, "dsh-codex: settings nav icon")
|
|
1307
1450
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-codex: client dictionaries")
|
|
1308
1451
|
const t = ctx.locale.bind(NS)
|
|
1309
1452
|
const client = createAuthorizationClient(ctx.connection)
|
|
@@ -1338,6 +1481,96 @@ window.__ModuleLoader__.load({
|
|
|
1338
1481
|
})
|
|
1339
1482
|
}
|
|
1340
1483
|
|
|
1484
|
+
function installSettingsNavIcon() {
|
|
1485
|
+
if (typeof document === "undefined" || typeof window === "undefined") return undefined
|
|
1486
|
+
const Observer = typeof window.MutationObserver === "function"
|
|
1487
|
+
? window.MutationObserver
|
|
1488
|
+
: typeof MutationObserver === "function" ? MutationObserver : undefined
|
|
1489
|
+
if (!document.body || typeof document.querySelectorAll !== "function" || Observer === undefined) return undefined
|
|
1490
|
+
|
|
1491
|
+
const isInsideSettingsNav = (target) => {
|
|
1492
|
+
const element = target?.nodeType === 1 ? target : target?.parentElement
|
|
1493
|
+
return typeof element?.closest === "function" && element.closest(SETTINGS_NAV_SELECTOR) !== null
|
|
1494
|
+
}
|
|
1495
|
+
const matchesOrContains = (node, selector) => (
|
|
1496
|
+
(typeof node?.matches === "function" && node.matches(selector))
|
|
1497
|
+
|| (typeof node?.querySelector === "function" && node.querySelector(selector) !== null)
|
|
1498
|
+
)
|
|
1499
|
+
const mutationTouchesSettingsNav = (records) => Array.from(records ?? []).some((record) => {
|
|
1500
|
+
if (isInsideSettingsNav(record.target)) return true
|
|
1501
|
+
return [...Array.from(record.addedNodes ?? []), ...Array.from(record.removedNodes ?? [])]
|
|
1502
|
+
.some((node) => matchesOrContains(node, SETTINGS_DIALOG_SELECTOR) || matchesOrContains(node, SETTINGS_NAV_SELECTOR))
|
|
1503
|
+
})
|
|
1504
|
+
const enqueueMicrotask = typeof window.queueMicrotask === "function"
|
|
1505
|
+
? (callback) => window.queueMicrotask(callback)
|
|
1506
|
+
: (callback) => Promise.resolve().then(callback)
|
|
1507
|
+
|
|
1508
|
+
let state = window[NAV_ICON_STATE_KEY]
|
|
1509
|
+
if (state === undefined) {
|
|
1510
|
+
let style = document.querySelector(NAV_ICON_STYLE_SELECTOR)
|
|
1511
|
+
if (style === null) {
|
|
1512
|
+
style = document.createElement("style")
|
|
1513
|
+
style.dataset.pluginNavIcon = "dsh-codex-community"
|
|
1514
|
+
document.head.appendChild(style)
|
|
1515
|
+
}
|
|
1516
|
+
style.dataset.plugin = "dsh-codex-community"
|
|
1517
|
+
const clearMarkers = () => {
|
|
1518
|
+
for (const button of document.querySelectorAll(`[${NAV_ICON_MARKER}]`)) button.removeAttribute(NAV_ICON_MARKER)
|
|
1519
|
+
}
|
|
1520
|
+
state = {
|
|
1521
|
+
active: true,
|
|
1522
|
+
refs: 0,
|
|
1523
|
+
scheduled: false,
|
|
1524
|
+
style,
|
|
1525
|
+
clearMarkers,
|
|
1526
|
+
scheduleSync() {
|
|
1527
|
+
if (!state.active || state.scheduled) return
|
|
1528
|
+
state.scheduled = true
|
|
1529
|
+
enqueueMicrotask(() => {
|
|
1530
|
+
state.scheduled = false
|
|
1531
|
+
state.sync()
|
|
1532
|
+
})
|
|
1533
|
+
},
|
|
1534
|
+
sync() {
|
|
1535
|
+
if (!state.active) return
|
|
1536
|
+
clearMarkers()
|
|
1537
|
+
const candidates = []
|
|
1538
|
+
for (const button of document.querySelectorAll(SETTINGS_NAV_BUTTON_SELECTOR)) {
|
|
1539
|
+
const icon = button.firstElementChild
|
|
1540
|
+
const labelElement = icon?.nextElementSibling
|
|
1541
|
+
if (icon?.localName !== "svg" || labelElement?.localName !== "span") continue
|
|
1542
|
+
const label = String(labelElement.textContent ?? "").replace(/\s+/gu, " ").trim()
|
|
1543
|
+
if (NAV_ICON_LABELS.has(label)) candidates.push(button)
|
|
1544
|
+
}
|
|
1545
|
+
if (candidates.length === 1) candidates[0].setAttribute(NAV_ICON_MARKER, "")
|
|
1546
|
+
},
|
|
1547
|
+
}
|
|
1548
|
+
state.observer = new Observer((records) => {
|
|
1549
|
+
if (mutationTouchesSettingsNav(records)) state.scheduleSync()
|
|
1550
|
+
})
|
|
1551
|
+
state.observer.observe(document.body, { childList: true, subtree: true, characterData: true })
|
|
1552
|
+
window[NAV_ICON_STATE_KEY] = state
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
state.refs += 1
|
|
1556
|
+
state.style.textContent = NAV_ICON_CSS
|
|
1557
|
+
state.sync()
|
|
1558
|
+
let active = true
|
|
1559
|
+
return () => {
|
|
1560
|
+
if (!active) return
|
|
1561
|
+
active = false
|
|
1562
|
+
if (window[NAV_ICON_STATE_KEY] !== state) return
|
|
1563
|
+
state.refs -= 1
|
|
1564
|
+
if (state.refs > 0) return
|
|
1565
|
+
state.active = false
|
|
1566
|
+
state.observer.disconnect()
|
|
1567
|
+
state.clearMarkers()
|
|
1568
|
+
if (typeof state.style.remove === "function") state.style.remove()
|
|
1569
|
+
else state.style.parentNode?.removeChild(state.style)
|
|
1570
|
+
delete window[NAV_ICON_STATE_KEY]
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1341
1574
|
function installStyle() {
|
|
1342
1575
|
if (typeof document === "undefined") return undefined
|
|
1343
1576
|
let style = document.querySelector(`style[data-plugin-css="${STYLE_ID}"]`)
|
|
@@ -1348,15 +1581,15 @@ window.__ModuleLoader__.load({
|
|
|
1348
1581
|
document.head.appendChild(style)
|
|
1349
1582
|
}
|
|
1350
1583
|
style.textContent = [
|
|
1351
|
-
".dshCodexPage{box-sizing:border-box;display:flex;width:100%;min-width:0;flex-direction:column;gap:
|
|
1584
|
+
".dshCodexPage{box-sizing:border-box;display:flex;width:100%;min-width:0;flex-direction:column;gap:12px;padding:4px 0 20px;color:var(--dsw-alias-label-primary);font:inherit}",
|
|
1352
1585
|
".dshCodexPage *{box-sizing:border-box}",
|
|
1353
1586
|
".dshCodexHero{display:flex;min-width:0;flex-direction:column;gap:8px}",
|
|
1354
1587
|
".dshCodexHero h2,.dshCodexHero p{margin:0}",
|
|
1355
1588
|
".dshCodexHero h2{font-size:16px;font-weight:500;line-height:24px}",
|
|
1356
1589
|
".dshCodexHero p{max-width:760px;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}",
|
|
1357
|
-
".dshCodexPanel{display:flex;width:100%;min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;gap:
|
|
1590
|
+
".dshCodexPanel{display:flex;width:100%;min-width:0;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px}",
|
|
1358
1591
|
".dshCodexPanel p,.dshCodexPanel label{overflow-wrap:anywhere;word-break:break-word}",
|
|
1359
|
-
".dshCodexCard{border:1px solid var(--dsw-alias-border-l2);border-radius:18px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-3));padding:
|
|
1592
|
+
".dshCodexCard{border:1px solid var(--dsw-alias-border-l2);border-radius:18px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-3));padding:18px}",
|
|
1360
1593
|
".dshCodexHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}",
|
|
1361
1594
|
".dshCodexHeader>div{min-width:0}",
|
|
1362
1595
|
".dshCodexHeader h3,.dshCodexHeader p,.dshCodexNotices p,.dshCodexQuota p{margin:0}",
|
|
@@ -1368,7 +1601,7 @@ window.__ModuleLoader__.load({
|
|
|
1368
1601
|
".dshCodexStatus[data-state=active] .dshCodexStatusDot{background:var(--dsw-alias-state-success-primary)}",
|
|
1369
1602
|
".dshCodexStatus[data-state=invalid] .dshCodexStatusDot{background:var(--dsw-alias-state-error-primary)}",
|
|
1370
1603
|
".dshCodexStatus[data-state=busy] .dshCodexStatusDot{background:var(--dsw-alias-state-business-primary)}",
|
|
1371
|
-
".dshCodexQuota{display:flex;min-width:0;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:
|
|
1604
|
+
".dshCodexQuota{display:flex;min-width:0;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:16px}",
|
|
1372
1605
|
".dshCodexQuotaHeader{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}",
|
|
1373
1606
|
".dshCodexQuota h3{margin:0;font-size:16px;font-weight:500;line-height:24px}",
|
|
1374
1607
|
".dshCodexQuotaProduct{font-size:14px;font-weight:500;line-height:22px}",
|
|
@@ -1377,25 +1610,29 @@ window.__ModuleLoader__.load({
|
|
|
1377
1610
|
".dshCodexQuotaUnknown::before{background:var(--dsw-alias-label-tertiary)}",
|
|
1378
1611
|
".dshCodexQuotaExhausted::before{background:var(--dsw-alias-state-error-primary)}",
|
|
1379
1612
|
".dshCodexQuotaExhausted{color:var(--dsw-alias-state-error-primary)}",
|
|
1380
|
-
".dshCodexQuotaGroup{display:flex;min-width:0;flex-direction:column;gap:
|
|
1613
|
+
".dshCodexQuotaGroup{display:flex;min-width:0;flex-direction:column;gap:8px}",
|
|
1381
1614
|
".dshCodexQuotaGroup+.dshCodexQuotaGroup{border-top:1px solid var(--dsw-alias-border-l2);padding-top:14px}",
|
|
1382
|
-
".
|
|
1383
|
-
".dshCodexQuotaWindow
|
|
1615
|
+
".dshCodexQuotaWindows{display:grid;min-width:0;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}",
|
|
1616
|
+
".dshCodexQuotaWindow{display:flex;min-width:0;flex-direction:column;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:11px 12px}",
|
|
1384
1617
|
".dshCodexQuotaWindowHeader,.dshCodexQuotaMeta{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}",
|
|
1385
1618
|
".dshCodexQuotaWindowHeader{font-size:14px;line-height:22px}",
|
|
1386
|
-
".dshCodexQuotaWindowHeader strong{font-weight:
|
|
1387
|
-
".dshCodexQuotaMeta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
|
|
1619
|
+
".dshCodexQuotaWindowHeader strong{font-weight:500;color:var(--dsw-alias-label-secondary)}",
|
|
1620
|
+
".dshCodexQuotaMeta{justify-content:flex-end;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
|
|
1621
|
+
".dshCodexQuotaMeta time{cursor:help}",
|
|
1388
1622
|
".dshCodexQuotaWindow progress{display:block;width:100%;height:8px;overflow:hidden;border:0;border-radius:999px;appearance:none;-webkit-appearance:none;background:var(--dsw-alias-bg-layer-1)}",
|
|
1389
1623
|
".dshCodexQuotaWindow progress::-webkit-progress-bar{border-radius:999px;background:var(--dsw-alias-bg-layer-1)}",
|
|
1390
1624
|
".dshCodexQuotaWindow progress::-webkit-progress-value{border-radius:999px;background:var(--dsw-alias-state-business-primary)}",
|
|
1391
1625
|
".dshCodexQuotaWindow progress::-moz-progress-bar{border-radius:999px;background:var(--dsw-alias-state-business-primary)}",
|
|
1392
|
-
".dshCodexQuotaUpdated{margin-top:2px!important}",
|
|
1393
1626
|
".dshCodexMuted{color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}",
|
|
1627
|
+
".dshCodexQuota .dshCodexQuotaUpdated{margin-top:2px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}",
|
|
1394
1628
|
".dshCodexWarning{max-width:820px;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:22px}",
|
|
1395
1629
|
".dshCodexActions{display:flex;min-width:0;flex-wrap:wrap;gap:8px}",
|
|
1396
1630
|
".dshCodexAuthActions{margin-left:auto;justify-content:flex-end}",
|
|
1397
1631
|
".dshCodexAuthActions .dshCodexButton{height:36px;min-height:36px;border-radius:18px;padding:0 14px;font-size:14px;line-height:22px}",
|
|
1398
|
-
".dshCodexQuotaHeader .dshCodexRefreshButton{min-height:
|
|
1632
|
+
".dshCodexQuotaHeader .dshCodexRefreshButton{display:inline-flex;min-height:30px;align-items:center;gap:6px;border:1px solid var(--dsw-alias-border-l2);border-radius:15px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);padding:0 10px;font-size:12px;line-height:18px;white-space:nowrap}",
|
|
1633
|
+
".dshCodexRefreshIcon{width:14px;height:14px;flex:none}",
|
|
1634
|
+
".dshCodexRefreshButton[data-loading=true] .dshCodexRefreshIcon{animation:dshCodexRefreshSpin .8s linear infinite}",
|
|
1635
|
+
"@keyframes dshCodexRefreshSpin{to{transform:rotate(360deg)}}",
|
|
1399
1636
|
".dshCodexButton{box-sizing:border-box;max-width:100%;white-space:normal;overflow-wrap:anywhere;word-break:break-word;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;padding:8px 14px;transition:background-color .15s ease,border-color .15s ease}",
|
|
1400
1637
|
".dshCodexButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}",
|
|
1401
1638
|
".dshCodexButton:focus-visible,.dshCodexModelOption:has(input:focus-visible){outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-border-l3));outline-offset:2px}",
|
|
@@ -1411,19 +1648,23 @@ window.__ModuleLoader__.load({
|
|
|
1411
1648
|
".dshCodexLegacyRepair:disabled{cursor:not-allowed;opacity:.55}",
|
|
1412
1649
|
".dshCodexPrimary{border-color:var(--dsw-alias-button-primary-fill,var(--dsw-alias-state-business-primary));background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-state-business-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}",
|
|
1413
1650
|
".dshCodexPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-state-business-primary))}",
|
|
1414
|
-
".dshCodexModelList{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
|
|
1415
|
-
".
|
|
1651
|
+
".dshCodexModelList{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:0;padding:0;list-style:none}",
|
|
1652
|
+
".dshCodexModelTitleRow{display:flex;min-width:0;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:4px 12px}",
|
|
1653
|
+
".dshCodexModelSummary{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;white-space:nowrap}",
|
|
1654
|
+
".dshCodexModelOption{display:flex;min-width:0;height:100%;align-items:flex-start;gap:10px;border:1px solid var(--dsw-alias-border-l2);border-radius:11px;background:var(--dsw-alias-bg-layer-1);padding:9px 10px;cursor:pointer;transition:background-color .15s ease,border-color .15s ease}",
|
|
1416
1655
|
".dshCodexModelOption:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}",
|
|
1656
|
+
".dshCodexModelOption[data-selected=true]{border-color:var(--dsw-alias-state-business-primary)}",
|
|
1417
1657
|
".dshCodexModelOption:has(input:disabled){cursor:not-allowed;opacity:.65}",
|
|
1418
1658
|
".dshCodexModelOption:has(input:disabled):hover{background:transparent}",
|
|
1419
1659
|
".dshCodexModelOption input{width:18px;height:18px;flex:none;margin:2px 0 0;accent-color:var(--dsw-alias-state-business-primary)}",
|
|
1420
|
-
".dshCodexModelCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:
|
|
1421
|
-
".dshCodexModelHeading{display:flex;min-width:0;flex-
|
|
1660
|
+
".dshCodexModelCopy{display:flex;min-width:0;flex:1;flex-direction:column;gap:5px}",
|
|
1661
|
+
".dshCodexModelHeading{display:flex;min-width:0;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:1px 8px}",
|
|
1422
1662
|
".dshCodexModelOption strong{overflow-wrap:anywhere;font-size:14px;font-weight:500;line-height:22px}",
|
|
1423
1663
|
".dshCodexModelId{overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font-family:inherit;font-size:12px;line-height:18px}",
|
|
1424
1664
|
".dshCodexModelCapabilities{display:flex;min-width:0;flex-wrap:wrap;gap:6px}",
|
|
1425
|
-
".dshCodexModelBadge{display:inline-flex;align-items:center;border-radius:999px;background:var(--dsw-alias-bg-layer-
|
|
1426
|
-
".
|
|
1665
|
+
".dshCodexModelBadge{display:inline-flex;align-items:center;border-radius:999px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-module-platform));color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12px;line-height:18px;padding:1px 7px}",
|
|
1666
|
+
".dshCodexModelToggle{align-self:center;border-radius:999px;padding:6px 12px;font-size:12px;line-height:18px}",
|
|
1667
|
+
".dshCodexModelActions{justify-content:flex-end;border-top:1px solid var(--dsw-alias-border-l2);padding-top:16px}",
|
|
1427
1668
|
".dshCodexNotices{display:flex;min-width:0;flex-direction:column;gap:10px;margin:0;padding:0;list-style:none}",
|
|
1428
1669
|
".dshCodexNotices li,.dshCodexPrompt{box-sizing:border-box;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:14px 16px}",
|
|
1429
1670
|
".dshCodexNotices a{display:inline-block;max-width:100%;margin-top:8px;color:var(--dsw-alias-state-business-primary)}",
|
|
@@ -1434,9 +1675,10 @@ window.__ModuleLoader__.load({
|
|
|
1434
1675
|
".dshCodexPrompt input,.dshCodexPrompt select{box-sizing:border-box;width:100%;min-width:0;height:38px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;padding:0 10px}",
|
|
1435
1676
|
".dshCodexError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}",
|
|
1436
1677
|
".dshCodexSuccess{margin:0;color:var(--dsw-alias-state-success-primary);font-size:12px;line-height:18px}",
|
|
1437
|
-
"@media(
|
|
1678
|
+
"@media(prefers-reduced-motion:reduce){.dshCodexRefreshButton[data-loading=true] .dshCodexRefreshIcon{animation:none}}",
|
|
1679
|
+
"@media(max-width:760px){.dshCodexQuotaWindows,.dshCodexModelList{grid-template-columns:1fr}.dshCodexModelActions{justify-content:flex-start}}",
|
|
1438
1680
|
"@media(max-width:640px){.dshCodexAuthToolbar,.dshCodexHeader{align-items:stretch;flex-direction:column}.dshCodexAuthActions{margin-left:0;justify-content:flex-start}}",
|
|
1439
|
-
"@media(max-width:480px){.dshCodexPage{gap:
|
|
1681
|
+
"@media(max-width:480px){.dshCodexPage{gap:10px;padding:0 0 16px}.dshCodexCard{border-radius:14px;padding:16px}.dshCodexActions{align-items:stretch;flex-direction:column}.dshCodexButton{width:100%}.dshCodexQuotaHeader .dshCodexRefreshButton{width:auto}.dshCodexQuotaWindowHeader{gap:8px}}",
|
|
1440
1682
|
].join("")
|
|
1441
1683
|
const references = style[STYLE_REF_KEY]
|
|
1442
1684
|
style[STYLE_REF_KEY] = Number.isSafeInteger(references) && references > 0
|
|
@@ -1468,7 +1710,9 @@ window.__ModuleLoader__.load({
|
|
|
1468
1710
|
exports.createAuthorizationClient = createAuthorizationClient
|
|
1469
1711
|
exports.createModelEnablementClient = createModelEnablementClient
|
|
1470
1712
|
exports.createSessionPreferenceClient = createSessionPreferenceClient
|
|
1713
|
+
exports.formatResetDistance = formatResetDistance
|
|
1471
1714
|
exports.inject = inject
|
|
1715
|
+
exports.installSettingsNavIcon = installSettingsNavIcon
|
|
1472
1716
|
exports.safeQuotaSnapshot = safeQuotaSnapshot
|
|
1473
1717
|
return module.exports
|
|
1474
1718
|
},
|
package/docs/README.en.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
- [Testing strategy](testing.en.md)
|
|
11
11
|
- [Release process](releasing.en.md)
|
|
12
12
|
- [GitHub About and topics](github-about.md)
|
|
13
|
+
- [v0.0.3 Release copy](releases/v0.0.3.md)
|
|
14
|
+
- [v0.0.2 Release copy](releases/v0.0.2.md)
|
|
13
15
|
- [v0.0.1 Release copy](releases/v0.0.1.md)
|
|
14
16
|
|
|
15
17
|
Every “verified” claim must point to repository tests, CI, or release-artifact evidence. “Wired” and “planned” must not be presented as “supported.”
|
package/docs/README.md
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
- [测试策略](testing.md)
|
|
11
11
|
- [发布流程](releasing.md)
|
|
12
12
|
- [GitHub About 与 Topics](github-about.md)
|
|
13
|
+
- [v0.0.3 Release 文案](releases/v0.0.3.md)
|
|
14
|
+
- [v0.0.2 Release 文案](releases/v0.0.2.md)
|
|
13
15
|
- [v0.0.1 Release 文案](releases/v0.0.1.md)
|
|
14
16
|
|
|
15
17
|
文档中的“已验证”必须能指向仓库内测试、CI 或发布产物证据;“已接线”和“计划中”不能写成“已支持”。
|
package/docs/compatibility.en.md
CHANGED
|
@@ -4,30 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
Last updated: 2026-08-29.
|
|
6
6
|
|
|
7
|
-
| Component or environment | `0.0.
|
|
7
|
+
| Component or environment | `0.0.3` candidate evidence | Status |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| DeepSeek Harness | `test/fixtures/dsh-runtime/pnpm-lock.yaml` locks the complete `@deepseek-ai/dsh@0.1.1-rc.2` runtime/peer graph and validates the `dsh-llm` schema and stream contract | Exact version verified |
|
|
10
|
-
| pi-ai | OAuth, catalog, Codex payload, and replay contract tests against `@earendil-works/pi-ai@0.82.1` | Public contract verified; 0.0.
|
|
11
|
-
| Node.js | Complete local suite on `22.22.2`; the [0.0.
|
|
10
|
+
| pi-ai | OAuth, catalog, Codex payload, and replay contract tests against `@earendil-works/pi-ai@0.82.1` | Public contract verified; 0.0.3 live-network acceptance is `0/13 pending` |
|
|
11
|
+
| Node.js | Complete local suite on `22.22.2`; the [0.0.3 three-platform candidate run](https://github.com/yoshino-xiao7/dsh-codex/actions/runs/33248224386) covers Node 22/24 | `>=22.19.0 <25`; candidate run passed |
|
|
12
12
|
| macOS | `macos-latest` Node 22/24 complete checks, frozen DSH installation, and Web/profile smoke | Platform gate passed |
|
|
13
13
|
| Windows x64 | `windows-latest` Node 22/24 complete checks, frozen DSH installation, and Web/profile smoke | Platform gate passed; real user environment pending |
|
|
14
14
|
| Linux x64 | `ubuntu-latest` Node 22/24 complete checks, frozen DSH installation, and Web/profile smoke | Platform gate passed |
|
|
15
15
|
| Real ChatGPT OAuth login | Automation does not read or modify a user's real grant | Controlled acceptance pending |
|
|
16
|
-
| Codex usage windows | Opening
|
|
16
|
+
| Codex usage windows | Opening settings and refreshing manually both make the Host actively read and strictly parse the real five-hour and weekly limits; local DSH settings verification covered remaining percentages, refresh state, the exact five-hour time, and the weekly 24-hour threshold | Verified locally; a failure retains the latest safe reading or falls back to request observation/an unknown state |
|
|
17
|
+
| Codex model settings | Web client tests and local Chinese/English settings verification cover installed-catalog fields, compact `K` labels, selected-first ordering, and collapsed unselected models | Verified locally; capabilities absent from the catalog are not inferred |
|
|
17
18
|
| Real Codex conversation | Automation does not consume user account quota | Controlled acceptance pending |
|
|
18
19
|
| Text / reasoning / usage / tools / replay | Public PiAiAdapter success, tool-call, and two-turn replay automation passes | Live reasoning, tool round trip, and conversation continuity are pending |
|
|
19
20
|
| Codex image input | Attachment-seam and budget-projection automation passes | A live request with `maxPixels=4194304` is pending |
|
|
20
21
|
| auto / SSE / WebSocket / cached | Transport mapping and session isolation pass in automation | One live request through each transport is pending |
|
|
21
22
|
| Fast / priority tier | Verified that only an explicit per-session choice changes `service_tier` | Account entitlement and live network pending |
|
|
22
|
-
| npm / GitHub Release | The strict workflow verifies the candidate, Registry readback, provenance, signatures, and Release assets; artifacts are attached to [`v0.0.
|
|
23
|
+
| npm / GitHub Release | The strict workflow verifies the candidate, Registry readback, provenance, signatures, and Release assets; artifacts are attached to [`v0.0.3`](https://github.com/yoshino-xiao7/dsh-codex/releases/tag/v0.0.3) | Formal publication is performed by the protected workflow |
|
|
23
24
|
|
|
24
|
-
The `0.0.x` line is a technical preview and declares compatibility only with the exact DSH release candidate above. Linux, macOS, and Windows CI/profile smoke for `0.0.
|
|
25
|
+
The `0.0.x` line is a technical preview and declares compatibility only with the exact DSH release candidate above. Linux, macOS, and Windows CI/profile smoke for `0.0.3` reached `3/3`. The accepted commit, exact Node versions, and job links are recorded in the [acceptance record](releases/v0.0.3.acceptance.json), with maintainer approval. Live OAuth, conversation, image, transport, and Fast network acceptance starts in a fresh `0/13 pending` record and will be completed individually post-release. These incomplete checks must remain visible and must never be presented as verified capabilities. Usage-window and model-setting behavior was verified independently in the local Chinese and English settings page and is not one of these 13 checks that consume a model request or require a complete interaction round trip.
|
|
25
26
|
|
|
26
27
|
The root `pnpm-lock.yaml` locks plugin dependencies, while `test/fixtures/dsh-runtime/pnpm-lock.yaml` independently locks the complete DSH runtime and peer graph used by compatibility smoke. CI runs `pnpm --dir test/fixtures/dsh-runtime install --frozen-lockfile --ignore-scripts` instead of handing that peer graph to direct npm resolution, avoiding nondeterministic dependency results and uncontrolled memory use. This frozen layer verifies Web/profile integration; it does not claim coverage of native terminal or native-build capabilities in DSH dependencies that require lifecycle scripts. Dependency upgrades must use a pinned-version PR, update and review both lockfiles, repeat complete CI, profile smoke, and supply-chain verification before publication, and renew controlled live validation afterward. The scheduled compatibility workflow only verifies the current locked graph and reports Registry drift; neither a broad semver range nor one scheduled run proves cross-RC compatibility.
|
|
27
28
|
|
|
28
29
|
## Profile composition
|
|
29
30
|
|
|
30
|
-
The `0.0.
|
|
31
|
+
The `0.0.3` bundle does not modify the general `llm-pi-ai` Cordis row. It provides:
|
|
31
32
|
|
|
32
33
|
- the `dsh-codex` route;
|
|
33
34
|
- the `dsh-codex` settings namespace;
|
|
@@ -46,9 +47,9 @@ Fast and transport preferences set by `/codex` exist only for the current proces
|
|
|
46
47
|
## Credentials and capability boundaries
|
|
47
48
|
|
|
48
49
|
- The Codex route accepts ChatGPT OAuth only and does not read an OpenAI Platform API key.
|
|
49
|
-
- Cancel sign-in through the plugin settings page or `/codex-login cancel`: cancellation succeeds before write linearization, while an in-progress commit is reported as too late and remains observed through its final state; `logout` always deletes the credential. The current DSH public seam has no post-commit cancellation barrier, so third-party code that calls `ctx.authorization.cancel()` directly bypasses this coordination and is not a supported `0.0.
|
|
50
|
+
- Cancel sign-in through the plugin settings page or `/codex-login cancel`: cancellation succeeds before write linearization, while an in-progress commit is reported as too late and remains observed through its final state; `logout` always deletes the credential. The current DSH public seam has no post-commit cancellation barrier, so third-party code that calls `ctx.authorization.cancel()` directly bypasses this coordination and is not a supported `0.0.3` interaction path.
|
|
50
51
|
- When a plugin or hot-reload flow owner is disposed, sign-in is cancelled before commit selection, while a selected credential commit is allowed to reach its final write before disposal completes. An unsupported or malformed stored record is shown as `invalid`, requires signing in again or signing out to clear it, and is never presented as signed in.
|
|
51
52
|
- The usage card actively reads the real five-hour and weekly limits when the settings page opens and when the user refreshes it manually. The Host returns only strictly parsed windows, percentages, and reset times to the Web page, never OAuth credentials or raw responses. If the endpoint is unavailable or malformed, the latest safe reading is retained and the card falls back to request observation or an unknown state rather than presenting the failure as zero remaining usage.
|
|
52
53
|
- DSH Web search uses its own provider and credentials; it does not reuse ChatGPT OAuth.
|
|
53
|
-
- `0.0.
|
|
54
|
+
- `0.0.3` does not provide image generation or editing.
|
|
54
55
|
- Session compaction continues to use DSH's built-in automatic compaction and `/compact`.
|
package/docs/compatibility.md
CHANGED
|
@@ -4,30 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
最近更新:2026-08-29。
|
|
6
6
|
|
|
7
|
-
| 组件或环境 | `0.0.
|
|
7
|
+
| 组件或环境 | `0.0.3` 候选证据 | 状态 |
|
|
8
8
|
| --- | --- | --- |
|
|
9
9
|
| DeepSeek Harness | `test/fixtures/dsh-runtime/pnpm-lock.yaml` 锁定完整 `@deepseek-ai/dsh@0.1.1-rc.2` runtime/peer 图,并验证 `dsh-llm` schema 与 stream contract | 精确版本已验证 |
|
|
10
|
-
| pi-ai | `@earendil-works/pi-ai@0.82.1` 的 OAuth、模型目录、Codex payload 与 replay 合同测试 | 公开合同已验证;0.0.
|
|
11
|
-
| Node.js | 本地 `22.22.2` 完整测试;[0.0.
|
|
10
|
+
| pi-ai | `@earendil-works/pi-ai@0.82.1` 的 OAuth、模型目录、Codex payload 与 replay 合同测试 | 公开合同已验证;0.0.3 真实网络验收 `0/13 pending` |
|
|
11
|
+
| Node.js | 本地 `22.22.2` 完整测试;[0.0.3 三平台候选运行](https://github.com/yoshino-xiao7/dsh-codex/actions/runs/33248224386)覆盖 Node 22/24 | `>=22.19.0 <25`;候选运行通过 |
|
|
12
12
|
| macOS | `macos-latest` Node 22/24 完整检查、冻结 DSH 安装与 Web/profile smoke | 平台门禁通过 |
|
|
13
13
|
| Windows x64 | `windows-latest` Node 22/24 完整检查、冻结 DSH 安装与 Web/profile smoke | 平台门禁通过;真实用户环境待验收 |
|
|
14
14
|
| Linux x64 | `ubuntu-latest` Node 22/24 完整检查、冻结 DSH 安装与 Web/profile smoke | 平台门禁通过 |
|
|
15
15
|
| ChatGPT OAuth 真实登录 | 自动化不读取或修改用户真实 grant | 受控验收待完成 |
|
|
16
|
-
| Codex 额度窗口 | 进入设置页和手动刷新都会通过 Host 主动读取并严格解析真实五小时与每周额度;本机 DSH
|
|
16
|
+
| Codex 额度窗口 | 进入设置页和手动刷新都会通过 Host 主动读取并严格解析真实五小时与每周额度;本机 DSH 设置页已验证剩余比例、刷新状态、五小时精确时间和每周 24 小时阈值 | 本机已验证;读取失败时保留最近安全读数或降级为请求观测/未知状态 |
|
|
17
|
+
| Codex 模型设置 | 安装目录字段、紧凑 `K` 标签、已选择优先与未选择折叠由 Web 客户端测试及本机中英文设置页覆盖 | 本机已验证;不推测目录未提供的能力 |
|
|
17
18
|
| Codex 真实网络对话 | 自动化不消耗用户账户配额 | 受控验收待完成 |
|
|
18
19
|
| 文本 / reasoning / usage / 工具 / replay | 公开 PiAiAdapter 成功流、工具调用和两轮 replay 自动化已通过 | 真实 reasoning、工具闭环与连续对话待验收 |
|
|
19
20
|
| Codex 图片输入 | attachment seam 与预算投影自动化已通过 | `maxPixels=4194304` 真实请求待验收 |
|
|
20
21
|
| auto / SSE / WebSocket / cached | transport 映射与会话隔离自动化已通过 | 四种真实请求待验收 |
|
|
21
22
|
| Fast / priority tier | 已验证仅在当前会话开启时改变 `service_tier` | 账号权限与真实网络待验收 |
|
|
22
|
-
| npm / GitHub Release | 严格工作流校验候选、Registry 回读、provenance、签名与 Release 资产;发布制品见 [`v0.0.
|
|
23
|
+
| npm / GitHub Release | 严格工作流校验候选、Registry 回读、provenance、签名与 Release 资产;发布制品见 [`v0.0.3`](https://github.com/yoshino-xiao7/dsh-codex/releases/tag/v0.0.3) | 正式发布由受保护工作流完成 |
|
|
23
24
|
|
|
24
|
-
`0.0.x` 是技术预览,只对表中的精确 DSH 预发布版本声明兼容。`0.0.
|
|
25
|
+
`0.0.x` 是技术预览,只对表中的精确 DSH 预发布版本声明兼容。`0.0.3` 的 Linux、macOS 和 Windows CI/profile smoke 已达到 `3/3`,候选提交、精确 Node 版本与作业链接记录在[验收记录](releases/v0.0.3.acceptance.json)中,并已获得维护者批准。真实 OAuth、对话、图片、transport 和 Fast 网络验收从独立的 `0/13 pending` 记录开始,发布后再逐项补齐;这些未完成项必须如实展示,不能冒充已验证能力。额度窗口与模型设置已独立完成本机中英文页面验证,不计入这 13 个会消耗模型请求或需要完整交互闭环的验收项。
|
|
25
26
|
|
|
26
27
|
根目录 `pnpm-lock.yaml` 锁定插件依赖,`test/fixtures/dsh-runtime/pnpm-lock.yaml` 独立锁定兼容性 smoke 的完整 DSH runtime 与 peer 图;CI 使用 `pnpm --dir test/fixtures/dsh-runtime install --frozen-lockfile --ignore-scripts`,不把该 peer 图交给直接 npm 解算,以避免不确定依赖结果和内存失控。该冻结层验证 Web/profile 集成,不声称覆盖 DSH 依赖中需要生命周期脚本的原生终端或本机构建能力。依赖升级必须通过固定版本变更 PR,同时更新并审查两套 lockfile,在发布前重跑完整 CI、profile smoke 和供应链校验,发布后重新记录受控真实验证。定时兼容工作流只验证当前锁定图并报告 Registry 漂移,不能从宽泛 semver 或一次定时运行推断跨 RC 兼容。
|
|
27
28
|
|
|
28
29
|
## Profile 组合
|
|
29
30
|
|
|
30
|
-
`0.0.
|
|
31
|
+
`0.0.3` 的 bundle 不修改通用 `llm-pi-ai` Cordis row。它提供:
|
|
31
32
|
|
|
32
33
|
- `dsh-codex` route;
|
|
33
34
|
- `dsh-codex` 设置命名空间;
|
|
@@ -46,9 +47,9 @@
|
|
|
46
47
|
## 凭据与功能边界
|
|
47
48
|
|
|
48
49
|
- Codex route 只接受 ChatGPT OAuth,不读取 OpenAI Platform API key;
|
|
49
|
-
- 请通过插件设置页或 `/codex-login cancel` 取消登录:写入线性化前会正常取消,提交已经开始时会明确提示无法取消并继续等待最终状态;`logout` 始终删除凭据。DSH 当前公共 seam 没有“提交后禁止取消”能力,第三方代码直接调用 `ctx.authorization.cancel()` 会绕过这一协调,不属于 `0.0.
|
|
50
|
+
- 请通过插件设置页或 `/codex-login cancel` 取消登录:写入线性化前会正常取消,提交已经开始时会明确提示无法取消并继续等待最终状态;`logout` 始终删除凭据。DSH 当前公共 seam 没有“提交后禁止取消”能力,第三方代码直接调用 `ctx.authorization.cancel()` 会绕过这一协调,不属于 `0.0.3` 支持的交互路径;
|
|
50
51
|
- 插件或热重载 flow owner 卸载时,提交选择前的登录会取消,已经进入凭据提交的登录会等待最终写入再完成卸载;不支持类型或结构损坏的已存记录显示为 `invalid`,需要重新登录或退出清除,不会显示为已登录;
|
|
51
52
|
- 额度卡会在进入设置页和手动刷新时主动读取真实五小时与每周额度;Host 只向 Web 页面返回严格解析后的窗口、百分比和重置时间,不返回 OAuth 凭据或原始响应。接口不可用或结构异常时保留最近一次安全读数,并降级为请求观测或未知状态,不把失败显示成零余额;
|
|
52
53
|
- DSH 的 Web search 使用它自己的 provider 和凭据,不复用 ChatGPT OAuth;
|
|
53
|
-
- `0.0.
|
|
54
|
+
- `0.0.3` 不提供图片生成或编辑;
|
|
54
55
|
- 会话压缩继续使用 DSH 自带的自动压缩和 `/compact`。
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 3,
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"releaseStatus": "approved",
|
|
5
|
+
"testedCommit": "e94f7a46ae028640655488ebd36d75bf1c157c96",
|
|
6
|
+
"approvedBy": "yoshino-xiao7",
|
|
7
|
+
"approvedAt": "2026-08-29T10:40:48Z",
|
|
8
|
+
"approvalEvidenceUrl": "https://github.com/yoshino-xiao7/dsh-codex/actions/runs/33248224386",
|
|
9
|
+
"platforms": {
|
|
10
|
+
"linux": {
|
|
11
|
+
"status": "passed",
|
|
12
|
+
"testedAt": "2026-08-29T10:37:22Z",
|
|
13
|
+
"runner": "GitHub Actions 1000001583",
|
|
14
|
+
"nodeVersion": "24.19.0",
|
|
15
|
+
"dshVersion": "0.1.1-rc.2",
|
|
16
|
+
"profileSmoke": "passed",
|
|
17
|
+
"evidenceUrl": "https://github.com/yoshino-xiao7/dsh-codex/actions/runs/33248224386/job/99089278195"
|
|
18
|
+
},
|
|
19
|
+
"macos": {
|
|
20
|
+
"status": "passed",
|
|
21
|
+
"testedAt": "2026-08-29T10:37:34Z",
|
|
22
|
+
"runner": "GitHub Actions 1000001585",
|
|
23
|
+
"nodeVersion": "24.18.0",
|
|
24
|
+
"dshVersion": "0.1.1-rc.2",
|
|
25
|
+
"profileSmoke": "passed",
|
|
26
|
+
"evidenceUrl": "https://github.com/yoshino-xiao7/dsh-codex/actions/runs/33248224386/job/99089278177"
|
|
27
|
+
},
|
|
28
|
+
"windows": {
|
|
29
|
+
"status": "passed",
|
|
30
|
+
"testedAt": "2026-08-29T10:39:06Z",
|
|
31
|
+
"runner": "GitHub Actions 1000001581",
|
|
32
|
+
"nodeVersion": "24.19.0",
|
|
33
|
+
"dshVersion": "0.1.1-rc.2",
|
|
34
|
+
"profileSmoke": "passed",
|
|
35
|
+
"evidenceUrl": "https://github.com/yoshino-xiao7/dsh-codex/actions/runs/33248224386/job/99089278146"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"liveAcceptance": {
|
|
39
|
+
"oauthWebSignIn": {
|
|
40
|
+
"status": "pending",
|
|
41
|
+
"testedAt": "TBD",
|
|
42
|
+
"evidenceUrl": "TBD",
|
|
43
|
+
"assertions": {
|
|
44
|
+
"flowStartedFromSettings": false,
|
|
45
|
+
"credentialConfigured": false,
|
|
46
|
+
"postSignInRequestSucceeded": false
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"modelCatalog": {
|
|
50
|
+
"status": "pending",
|
|
51
|
+
"testedAt": "TBD",
|
|
52
|
+
"evidenceUrl": "TBD",
|
|
53
|
+
"assertions": {
|
|
54
|
+
"settingsCatalogVisible": false,
|
|
55
|
+
"conversationModelSelectable": false
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"textStream": {
|
|
59
|
+
"status": "pending",
|
|
60
|
+
"testedAt": "TBD",
|
|
61
|
+
"evidenceUrl": "TBD",
|
|
62
|
+
"assertions": {
|
|
63
|
+
"nonEmptyTextDelta": false,
|
|
64
|
+
"terminalStopObserved": false
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"reasoningStream": {
|
|
68
|
+
"status": "pending",
|
|
69
|
+
"testedAt": "TBD",
|
|
70
|
+
"evidenceUrl": "TBD",
|
|
71
|
+
"assertions": {
|
|
72
|
+
"reasoningBlockObserved": false,
|
|
73
|
+
"terminalStopObserved": false
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"terminalUsage": {
|
|
77
|
+
"status": "pending",
|
|
78
|
+
"testedAt": "TBD",
|
|
79
|
+
"evidenceUrl": "TBD",
|
|
80
|
+
"assertions": {
|
|
81
|
+
"inputTokensObserved": false,
|
|
82
|
+
"outputTokensObserved": false
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"toolRoundTrip": {
|
|
86
|
+
"status": "pending",
|
|
87
|
+
"testedAt": "TBD",
|
|
88
|
+
"evidenceUrl": "TBD",
|
|
89
|
+
"assertions": {
|
|
90
|
+
"toolCallObserved": false,
|
|
91
|
+
"toolExecuted": false,
|
|
92
|
+
"toolResultReturned": false,
|
|
93
|
+
"followUpSucceeded": false
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"replayContinuity": {
|
|
97
|
+
"status": "pending",
|
|
98
|
+
"testedAt": "TBD",
|
|
99
|
+
"evidenceUrl": "TBD",
|
|
100
|
+
"assertions": {
|
|
101
|
+
"firstTurnSucceeded": false,
|
|
102
|
+
"secondTurnUsedPriorContext": false,
|
|
103
|
+
"secondTurnSucceeded": false
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"imageMaxPixels": {
|
|
107
|
+
"status": "pending",
|
|
108
|
+
"testedAt": "TBD",
|
|
109
|
+
"evidenceUrl": "TBD",
|
|
110
|
+
"assertions": {
|
|
111
|
+
"nativeImageAccepted": false,
|
|
112
|
+
"maxPixels4194304Projected": false,
|
|
113
|
+
"requestSucceeded": false
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"transportAuto": {
|
|
117
|
+
"status": "pending",
|
|
118
|
+
"testedAt": "TBD",
|
|
119
|
+
"evidenceUrl": "TBD",
|
|
120
|
+
"assertions": {
|
|
121
|
+
"requestSucceeded": false
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
"transportSse": {
|
|
125
|
+
"status": "pending",
|
|
126
|
+
"testedAt": "TBD",
|
|
127
|
+
"evidenceUrl": "TBD",
|
|
128
|
+
"assertions": {
|
|
129
|
+
"requestSucceeded": false
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"transportWebsocket": {
|
|
133
|
+
"status": "pending",
|
|
134
|
+
"testedAt": "TBD",
|
|
135
|
+
"evidenceUrl": "TBD",
|
|
136
|
+
"assertions": {
|
|
137
|
+
"requestSucceeded": false
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"transportWebsocketCached": {
|
|
141
|
+
"status": "pending",
|
|
142
|
+
"testedAt": "TBD",
|
|
143
|
+
"evidenceUrl": "TBD",
|
|
144
|
+
"assertions": {
|
|
145
|
+
"firstTurnSucceeded": false,
|
|
146
|
+
"secondTurnSucceeded": false
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"fastPriority": {
|
|
150
|
+
"status": "pending",
|
|
151
|
+
"testedAt": "TBD",
|
|
152
|
+
"evidenceUrl": "TBD",
|
|
153
|
+
"assertions": {
|
|
154
|
+
"priorityRequested": false,
|
|
155
|
+
"requestSucceeded": false,
|
|
156
|
+
"noAutomaticDowngrade": false
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
> 发布日期 / Release date:2026-08-29
|
|
2
|
+
> 验收提交 / Accepted commit:e94f7a46ae028640655488ebd36d75bf1c157c96
|
|
3
|
+
> npm:`dsh-codex-community@0.0.3`
|
|
4
|
+
> 发布方式 / Publication:npm `latest` + 正式 GitHub Release / full GitHub Release
|
|
5
|
+
> 平台验收 / Platform acceptance:3/3
|
|
6
|
+
> 发布后真实账号验证 / Post-release live validation:0/13
|
|
7
|
+
|
|
8
|
+
## 中文
|
|
9
|
+
|
|
10
|
+
### 变更
|
|
11
|
+
|
|
12
|
+
- OpenAI Codex 设置入口改用独立的代码括号图标;替换只作用于唯一匹配的 Codex 导航项,并在热重载或卸载时完整清理。
|
|
13
|
+
- 额度区改为响应式双列卡片,统一显示“剩余”语义;刷新操作具有图标、刷新中状态和无障碍 busy 标记,并遵循系统的减少动态效果偏好。
|
|
14
|
+
- 五小时额度始终显示精确重置时间;每周额度在距离重置不足 24 小时时显示精确时间,其余时间显示本地化的相对天数。完整时间仍保留在提示与无障碍标签中。
|
|
15
|
+
- 模型列表优先显示已选择模型,未选择模型默认收起并可按需展开;页面同步显示已选择、当前显示与总模型数。
|
|
16
|
+
- 上下文与最大输出使用 `272K`、`128K` 一类紧凑标签,只展示当前安装的 provider catalog 实际提供的字段,不推测额外能力。
|
|
17
|
+
- 设置内容不再覆盖 Harness 弹窗宽度或滚动容器;桌面使用双列、窄屏回落为单列,减少弹窗高度并改善滚动。
|
|
18
|
+
- 在收起状态取消选择模型后,键盘焦点会回到展开按钮;重置时间解析会拒绝无法构造有效日期的值。
|
|
19
|
+
|
|
20
|
+
### 兼容性
|
|
21
|
+
|
|
22
|
+
- 精确兼容目标保持为 DeepSeek Harness `0.1.1-rc.2` 与 Node.js `>=22.19.0 <25`,不承诺兼容其他 DSH 预发布版本。
|
|
23
|
+
- 本版本仅调整 Web 设置页 bundle 与对应测试,不改变 OAuth、Provider、传输或模型设置持久化合同。
|
|
24
|
+
- 完整检查与 Linux/macOS/Windows `3/3` 已绑定候选提交并由维护者批准;正式工作流仍会在发布前后校验同一候选、供应链证据、Registry 与 GitHub Release。发布后真实账号验收从独立的 `0.0.3` 记录以 `0/13` 开始,不继承旧版本证据。
|
|
25
|
+
- 本版本不提供图片生成或编辑。
|
|
26
|
+
|
|
27
|
+
### 验收记录
|
|
28
|
+
|
|
29
|
+
- [`v0.0.3.acceptance.json`](v0.0.3.acceptance.json)
|
|
30
|
+
|
|
31
|
+
### 制品校验
|
|
32
|
+
|
|
33
|
+
- SHA-256:正式工作流生成并随 GitHub Release 附带校验文件。
|
|
34
|
+
- SRI:正式工作流生成、Registry 回读并随 GitHub Release 附带校验文件。
|
|
35
|
+
|
|
36
|
+
## English
|
|
37
|
+
|
|
38
|
+
### Changes
|
|
39
|
+
|
|
40
|
+
- The OpenAI Codex settings entry now uses a dedicated code-brackets icon. Replacement is limited to the uniquely matched Codex navigation item and is fully cleaned up on hot reload or disposal.
|
|
41
|
+
- Usage is presented in responsive two-column cards with a consistent “remaining” meaning. Refresh has an icon, a visible loading state, and accessible busy semantics, and its animation respects the reduced-motion preference.
|
|
42
|
+
- Five-hour usage always shows the exact reset time. Weekly usage shows the exact time within 24 hours and otherwise uses localized relative days; the complete timestamp remains available in tooltips and accessible labels.
|
|
43
|
+
- Selected models are listed first, while unselected models are collapsed by default and can be expanded on demand. The page also reports selected, visible, and total model counts.
|
|
44
|
+
- Context and maximum output use compact labels such as `272K` and `128K`. Only fields actually supplied by the installed provider catalog are shown; no additional capabilities are inferred.
|
|
45
|
+
- The settings content no longer overrides the Harness dialog width or scroll container. It uses two columns on desktop and one on narrow screens, reducing height and improving scrolling.
|
|
46
|
+
- Deselecting a model while unselected entries are collapsed returns keyboard focus to the expansion control. Reset-time parsing also rejects values that cannot produce a valid date.
|
|
47
|
+
|
|
48
|
+
### Compatibility
|
|
49
|
+
|
|
50
|
+
- The exact compatibility target remains DeepSeek Harness `0.1.1-rc.2` with Node.js `>=22.19.0 <25`; compatibility with other DSH prereleases is not claimed.
|
|
51
|
+
- This release changes only the Web settings bundle and its tests. OAuth, Provider, transport, and model-setting persistence contracts are unchanged.
|
|
52
|
+
- Full checks and Linux/macOS/Windows `3/3` are bound to the accepted candidate and approved by the maintainer. The protected workflow still verifies the same candidate, supply-chain evidence, the Registry, and the GitHub Release before and after publication. Post-release live-account acceptance starts at `0/13` in a fresh `0.0.3` record and does not inherit evidence from an older release.
|
|
53
|
+
- This release does not provide image generation or editing.
|
|
54
|
+
|
|
55
|
+
### Acceptance record
|
|
56
|
+
|
|
57
|
+
- [`v0.0.3.acceptance.json`](v0.0.3.acceptance.json)
|
|
58
|
+
|
|
59
|
+
### Artifact verification
|
|
60
|
+
|
|
61
|
+
- SHA-256: generated by the protected release workflow and attached to the GitHub Release.
|
|
62
|
+
- SRI: generated by the protected release workflow, read back from the Registry, and attached to the GitHub Release.
|