@eddyskywalker/dsh-chatgpt-subscription 0.1.10 → 0.1.11
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 +9 -0
- package/README.md +17 -7
- package/lib/client.js +442 -28
- package/lib/client.js.map +1 -1
- package/lib/index.js +718 -247
- package/lib/types/client/CodexComposerQuota.d.ts +13 -0
- package/lib/types/client/CodexComposerQuota.d.ts.map +1 -0
- package/lib/types/client/CodexImageToolView.d.ts +10 -0
- package/lib/types/client/CodexImageToolView.d.ts.map +1 -0
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/api.d.ts +2 -1
- package/lib/types/client/api.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +57 -3
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/quota.d.ts +9 -0
- package/lib/types/client/quota.d.ts.map +1 -0
- package/lib/types/client/styles.d.ts.map +1 -1
- package/lib/types/compat.d.ts +7 -0
- package/lib/types/compat.d.ts.map +1 -1
- package/lib/types/host/codex-images.d.ts +9 -0
- package/lib/types/host/codex-images.d.ts.map +1 -0
- package/lib/types/host/codex-search.d.ts +11 -0
- package/lib/types/host/codex-search.d.ts.map +1 -0
- package/lib/types/host/model-catalog.d.ts.map +1 -1
- package/lib/types/host/preferences.d.ts +12 -0
- package/lib/types/host/preferences.d.ts.map +1 -0
- package/lib/types/host/responses-mapper.d.ts.map +1 -1
- package/lib/types/host/routes.d.ts +2 -1
- package/lib/types/host/routes.d.ts.map +1 -1
- package/lib/types/host/search-provider-switcher.d.ts +15 -0
- package/lib/types/host/search-provider-switcher.d.ts.map +1 -0
- package/lib/types/host/usage-service.d.ts +2 -1
- package/lib/types/host/usage-service.d.ts.map +1 -1
- package/lib/types/index.d.ts +3 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/contracts.d.ts +40 -3
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/lib/types/shared/model-catalog.d.ts +81 -0
- package/lib/types/shared/model-catalog.d.ts.map +1 -0
- package/lib/types/shared/preferences.d.ts +7 -0
- package/lib/types/shared/preferences.d.ts.map +1 -0
- package/package.json +21 -2
- package/lib/types/host/subagent-report-scheduling-compat.d.ts +0 -21
- package/lib/types/host/subagent-report-scheduling-compat.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.11 - 2026-08-18
|
|
4
|
+
|
|
5
|
+
- 移除临时子代理报告去重兼容模块:按照标记 `DSH_COMPAT_REMOVE(subagent-report-settlement-dedup)` 安全移除 `subagent-report-scheduling-compat` 模块、相关测试及插件中的 `agents` 依赖注入与挂钩清理逻辑。
|
|
6
|
+
- 扩展 Codex usage 解析,支持 `additional_rate_limits`、Credits、月度消费控制与 reset credits,并在设置页展示。
|
|
7
|
+
- 新增 ChatGPT 订阅侧 Codex 搜索 provider,可在设置页切换 DSH 默认搜索或 `codex-subscription` 搜索来源。
|
|
8
|
+
- 新增 `codex_image_generate` 图片生成工具,使用 Codex 图片 endpoint 生成 PNG 并保存为 DSH 图片附件,同时提供会话内工具结果渲染。
|
|
9
|
+
- 新增 composer 快捷用量徽标开关;当前会话选择 `codex-chatgpt` 模型时,可显示最紧张限额窗口的剩余额度。
|
|
10
|
+
- 统一模型目录显示名与 Codex App 风格:`5.6 Sol`、`5.6 Terra`、`5.6 Luna`、`5.5`、`5.4`、`5.4 Mini`、`5.3 Codex Spark`,并为 Spark 避免发送不兼容的 `reasoning.summary`。
|
|
11
|
+
|
|
3
12
|
## 0.1.10 - 2026-08-17
|
|
4
13
|
|
|
5
14
|
- 移除临时 Bash Prompt 兼容模块:由于上游 DSH `0.1.0-rc.7` 已经原生修复持久化 Bash 提示符不匹配与重置问题,现按照标记 `DSH_COMPAT_REMOVE(persistent-bash-prompt-mismatch)` 安全移除全部相关兼容代码与测试。保留子代理报告去重兼容模块 `DSH_COMPAT_REMOVE(subagent-report-settlement-dedup)`。
|
package/README.md
CHANGED
|
@@ -31,17 +31,27 @@
|
|
|
31
31
|
- 固定 Codex Responses 地址,支持流式文本、reasoning summary、图片输入与工具调用/结果;
|
|
32
32
|
- 原样转发 DSH 暴露的工具 schema;命令工具兼容 `pwsh` / `powershell`、`bash`、`sh` 与 `shell`,并按 PowerShell、Bash 或 POSIX sh 注入对应说明;
|
|
33
33
|
- 429/5xx 由 DSH retry policy 接管;401 只强制刷新并重试一次,支持 `AbortSignal`;
|
|
34
|
-
- Codex
|
|
35
|
-
-
|
|
34
|
+
- Codex、Code review 及上游返回的额外窗口额度,支持 Credits、月度消费控制与 reset credits 展示;60 秒缓存、15 秒上游节流并遵守 `Retry-After`;
|
|
35
|
+
- 提供 ChatGPT 订阅侧 Codex 搜索 provider,可在设置页切换 DSH 默认搜索或 Codex 订阅搜索;
|
|
36
|
+
- 新增 `codex_image_generate` 工具,生成图片后通过 DSH 附件系统保存并在会话中渲染;
|
|
37
|
+
- 可选 composer 快捷用量徽标,按当前 `codex-chatgpt` 模型显示最紧张窗口的剩余额度。
|
|
36
38
|
|
|
37
39
|
**设置页**
|
|
38
40
|
|
|
39
|
-
- 展示账号(脱敏 email、套餐、账号 ID
|
|
41
|
+
- 展示账号(脱敏 email、套餐、账号 ID 后四位)、连接状态、额度与订阅增强功能开关;
|
|
40
42
|
- 可访问的进度条、窄窗口/200% 缩放布局、深浅主题与 reduced-motion。
|
|
41
43
|
|
|
42
44
|
## 模型目录
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
| 显示名 | 模型 slug |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| 5.6 Sol | `gpt-5.6-sol` |
|
|
49
|
+
| 5.6 Terra | `gpt-5.6-terra` |
|
|
50
|
+
| 5.6 Luna | `gpt-5.6-luna` |
|
|
51
|
+
| 5.5 | `gpt-5.5` |
|
|
52
|
+
| 5.4 | `gpt-5.4` |
|
|
53
|
+
| 5.4 Mini | `gpt-5.4-mini` |
|
|
54
|
+
| 5.3 Codex Spark | `gpt-5.3-codex-spark` |
|
|
45
55
|
|
|
46
56
|
> 目录只用于展示;账号实际可用的模型由 ChatGPT 套餐、workspace 策略与上游兼容状态决定。
|
|
47
57
|
|
|
@@ -113,7 +123,7 @@ DSH 模型选择器应显示 **“Codex(ChatGPT 订阅)”**。
|
|
|
113
123
|
|
|
114
124
|
- OAuth 回调固定为 `http://localhost:1455/auth/callback`,登录任务五分钟超时,同一时刻只允许一个;
|
|
115
125
|
- OAuth token 只发送到 `https://auth.openai.com/oauth/token`;
|
|
116
|
-
-
|
|
126
|
+
- 模型、图片、搜索与额度地址分别固定为 `https://chatgpt.com/backend-api/codex/responses`、`https://chatgpt.com/backend-api/codex/images/generations`、`https://chatgpt.com/backend-api/codex/alpha/search` 和 `https://chatgpt.com/backend-api/wham/usage`,没有 endpoint override;
|
|
117
127
|
- Windows token bundle 使用 CurrentUser DPAPI;明文只经过 Host 内存和 PowerShell stdin/stdout;
|
|
118
128
|
- Linux token bundle 原子写入当前用户私有文件并在读取时校验普通文件、所有者与 `0600` 权限,同时拒绝符号链接;该文件没有应用层加密,同 UID 进程、root、备份和磁盘快照仍可读取;
|
|
119
129
|
- 两个平台的 token 都不会进入浏览器、`settings.yaml` 或日志;
|
|
@@ -134,6 +144,7 @@ DSH 模型选择器应显示 **“Codex(ChatGPT 订阅)”**。
|
|
|
134
144
|
| POST | `/token/refresh` | 刷新 token |
|
|
135
145
|
| POST | `/quota/refresh` | 刷新额度 |
|
|
136
146
|
| POST | `/connection/test` | 测试连接 |
|
|
147
|
+
| POST | `/preferences/update` | 更新搜索来源和 composer 快捷用量偏好 |
|
|
137
148
|
|
|
138
149
|
状态响应只包含脱敏 email、套餐、账号 ID 后四位、token 到期时间和额度 DTO。
|
|
139
150
|
|
|
@@ -159,5 +170,4 @@ npm pack --dry-run
|
|
|
159
170
|
| 模型不可用 | 检查 ChatGPT 套餐、workspace 权限与当前模型可用性 |
|
|
160
171
|
| DPAPI 读取失败 | 确认 DSH 以创建凭据时的同一 Windows 用户运行;必要时清理凭据后重新登录 |
|
|
161
172
|
| Linux 凭据存储不可用 | 确认凭据属于当前用户且权限为 `0600`,父目录权限为 `0700`;修复权限或注销后重新登录 |
|
|
162
|
-
| Linux 上工具调用语法错误 | 确认 DSH 暴露的是 `bash`、`sh` 或 `shell`,并使用相应的 Bash/POSIX 语法与 `/` 路径 |
|
|
163
|
-
| 父 Agent 已收到子代理结果,但相同报告仍显示为排队消息 | 本插件为 DSH `0.1.0-rc.6` 提供临时 report/settlement 精确去重;升级或重载插件并新建会话后验证。若未来 DSH 已原生修复,可移除所有 `DSH_COMPAT_REMOVE(subagent-report-settlement-dedup)` 标记代码 |
|
|
173
|
+
| Linux 上工具调用语法错误 | 确认 DSH 暴露的是 `bash`、`sh` 或 `shell`,并使用相应的 Bash/POSIX 语法与 `/` 路径 |
|
package/lib/client.js
CHANGED
|
@@ -7,6 +7,242 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
9
|
const ROUTE_PREFIX = "/api/dsh-chatgpt-subscription";
|
|
10
|
+
const CODEX_CHATGPT_PROVIDER_ID = "codex-chatgpt";
|
|
11
|
+
const CODEX_IMAGE_TOOL_NAME = "codex_image_generate";
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/client/quota.ts
|
|
14
|
+
function selectQuotaForModel(quota, modelId) {
|
|
15
|
+
if (quota === void 0 || quota.buckets.length === 0) return null;
|
|
16
|
+
const bucket = (modelId?.toLowerCase() ?? "").includes("spark") ? quota.buckets.find((candidate) => `${candidate.id} ${candidate.name}`.toLowerCase().includes("spark")) : quota.buckets.find((candidate) => candidate.id === "codex") ?? quota.buckets.find((candidate) => candidate.name.toLowerCase() === "codex");
|
|
17
|
+
if (bucket === void 0) return null;
|
|
18
|
+
const windows = quotaWindows(bucket);
|
|
19
|
+
if (windows.length === 0) return null;
|
|
20
|
+
const window = windows.reduce((tightest, candidate) => candidate.usedPercent > tightest.usedPercent ? candidate : tightest, windows[0]);
|
|
21
|
+
return {
|
|
22
|
+
bucket,
|
|
23
|
+
window,
|
|
24
|
+
remainingPercent: Math.max(0, 100 - window.usedPercent)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function quotaWindows(bucket) {
|
|
28
|
+
if (bucket.windows.length > 0) return bucket.windows;
|
|
29
|
+
return [bucket.primary, bucket.secondary].filter((window) => window !== null);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/client/CodexComposerQuota.tsx
|
|
33
|
+
function CodexComposerQuota({ api, directory, loadModelDirectory, t }) {
|
|
34
|
+
const modelState = useStore(directory);
|
|
35
|
+
const [status, setStatus] = (0, react.useState)(null);
|
|
36
|
+
const [loading, setLoading] = (0, react.useState)(false);
|
|
37
|
+
const mountedRef = (0, react.useRef)(false);
|
|
38
|
+
const selected = modelState.current;
|
|
39
|
+
const isCodex = selected?.provider === CODEX_CHATGPT_PROVIDER_ID;
|
|
40
|
+
(0, react.useEffect)(() => {
|
|
41
|
+
loadModelDirectory();
|
|
42
|
+
}, [loadModelDirectory]);
|
|
43
|
+
(0, react.useEffect)(() => {
|
|
44
|
+
mountedRef.current = true;
|
|
45
|
+
return () => {
|
|
46
|
+
mountedRef.current = false;
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
(0, react.useEffect)(() => {
|
|
50
|
+
if (!isCodex) return;
|
|
51
|
+
let disposed = false;
|
|
52
|
+
const refresh = async () => {
|
|
53
|
+
setLoading(true);
|
|
54
|
+
try {
|
|
55
|
+
const next = await api.status();
|
|
56
|
+
if (!disposed && mountedRef.current) setStatus(next);
|
|
57
|
+
} catch {
|
|
58
|
+
if (!disposed && mountedRef.current) setStatus(null);
|
|
59
|
+
} finally {
|
|
60
|
+
if (!disposed && mountedRef.current) setLoading(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
refresh();
|
|
64
|
+
const timer = window.setInterval(() => {
|
|
65
|
+
if (document.visibilityState === "visible") refresh();
|
|
66
|
+
}, 6e4);
|
|
67
|
+
return () => {
|
|
68
|
+
disposed = true;
|
|
69
|
+
window.clearInterval(timer);
|
|
70
|
+
};
|
|
71
|
+
}, [api, isCodex]);
|
|
72
|
+
const quota = (0, react.useMemo)(() => selectQuotaForModel(status?.quota, selected?.model), [selected?.model, status?.quota]);
|
|
73
|
+
if (!isCodex || status?.preferences.quickQuotaVisible !== true) return null;
|
|
74
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
75
|
+
className: "dsh-codex-composer-quota",
|
|
76
|
+
"data-level": quota === null ? "normal" : quota.remainingPercent <= 5 ? "danger" : quota.remainingPercent <= 20 ? "warning" : "normal",
|
|
77
|
+
"aria-label": quota === null ? t("quickQuotaLoading") : `${t("quickQuotaLabel")}: ${formatPercent$1(quota.remainingPercent)}`,
|
|
78
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("quickQuotaLabel") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: quota === null ? loading ? t("quickQuotaLoading") : "—" : formatPercent$1(quota.remainingPercent) })]
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function useStore(store) {
|
|
82
|
+
return (0, react.useSyncExternalStore)((listener) => store.subscribe(listener), () => store.getSnapshot(), () => store.getSnapshot());
|
|
83
|
+
}
|
|
84
|
+
function formatPercent$1(value) {
|
|
85
|
+
return `${new Intl.NumberFormat(void 0, { maximumFractionDigits: 0 }).format(value)}%`;
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/client/CodexImageToolView.tsx
|
|
89
|
+
function CodexImageToolView({ block, loadImage, t }) {
|
|
90
|
+
const settled = "kind" in block;
|
|
91
|
+
const isError = settled ? block.isError : false;
|
|
92
|
+
const prompt = promptFromBlock(block);
|
|
93
|
+
const images = settled && !isError ? imageBlocks(block.content) : [];
|
|
94
|
+
const label = isError ? t("imageToolFailed") : settled ? t("imageToolDone") : t("imageToolRunning");
|
|
95
|
+
const summary = prompt ?? textSummary(settled ? block.content : []);
|
|
96
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
97
|
+
className: "dsh-codex-image-tool",
|
|
98
|
+
"data-state": isError ? "error" : settled ? "done" : "running",
|
|
99
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
100
|
+
className: "dsh-codex-image-tool-head",
|
|
101
|
+
children: [
|
|
102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
103
|
+
className: "dsh-codex-image-dot",
|
|
104
|
+
"aria-hidden": "true"
|
|
105
|
+
}),
|
|
106
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
107
|
+
className: "dsh-codex-image-title",
|
|
108
|
+
children: label
|
|
109
|
+
}),
|
|
110
|
+
summary !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
111
|
+
className: "dsh-codex-image-summary",
|
|
112
|
+
children: summary
|
|
113
|
+
}) : null
|
|
114
|
+
]
|
|
115
|
+
}), images.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
116
|
+
className: "dsh-codex-image-grid",
|
|
117
|
+
children: images.map((attachment) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GeneratedImage, {
|
|
118
|
+
attachment,
|
|
119
|
+
load: loadImage,
|
|
120
|
+
label: attachment.name ?? t("image"),
|
|
121
|
+
t
|
|
122
|
+
}, String(attachment.attachmentId)))
|
|
123
|
+
}) : null]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function GeneratedImage({ attachment, load, label, t }) {
|
|
127
|
+
const [src, setSrc] = (0, react.useState)(null);
|
|
128
|
+
const [failed, setFailed] = (0, react.useState)(false);
|
|
129
|
+
(0, react.useEffect)(() => {
|
|
130
|
+
let disposed = false;
|
|
131
|
+
setSrc(null);
|
|
132
|
+
setFailed(false);
|
|
133
|
+
load(attachment).then((url) => {
|
|
134
|
+
if (!disposed) setSrc(url);
|
|
135
|
+
}, () => {
|
|
136
|
+
if (!disposed) setFailed(true);
|
|
137
|
+
});
|
|
138
|
+
return () => {
|
|
139
|
+
disposed = true;
|
|
140
|
+
};
|
|
141
|
+
}, [attachment, load]);
|
|
142
|
+
if (failed) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
143
|
+
className: "dsh-codex-image-failed",
|
|
144
|
+
children: t("imageLoadFailed")
|
|
145
|
+
});
|
|
146
|
+
if (src === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
147
|
+
className: "dsh-codex-image-loading",
|
|
148
|
+
children: t("imageLoading")
|
|
149
|
+
});
|
|
150
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
151
|
+
className: "dsh-codex-image-preview",
|
|
152
|
+
src,
|
|
153
|
+
alt: label
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function promptFromBlock(block) {
|
|
157
|
+
const raw = "kind" in block ? block.call?.argsRaw : block.argsRaw;
|
|
158
|
+
if (typeof raw !== "string" || raw.trim() === "") return null;
|
|
159
|
+
try {
|
|
160
|
+
const value = JSON.parse(raw);
|
|
161
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.prompt === "string" ? value.prompt : null;
|
|
162
|
+
} catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function imageBlocks(blocks) {
|
|
167
|
+
const result = [];
|
|
168
|
+
for (const block of blocks) {
|
|
169
|
+
if (block.type === "image") result.push(block.attachment);
|
|
170
|
+
if (block.type === "tool-result") result.push(...imageBlocks(block.content));
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
function textSummary(blocks) {
|
|
175
|
+
const text = blocks.map((block) => block.type === "text" || block.type === "reasoning" ? block.text : "").filter(Boolean).join(" ").trim();
|
|
176
|
+
return text === "" ? null : text;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/shared/model-catalog.ts
|
|
180
|
+
const CODEX_MODEL_CATALOG = [
|
|
181
|
+
{
|
|
182
|
+
id: "gpt-5.6-sol",
|
|
183
|
+
name: "5.6 Sol",
|
|
184
|
+
contextWindow: 272e3,
|
|
185
|
+
inputModalities: ["text", "image"],
|
|
186
|
+
defaultReasoningEffort: "medium",
|
|
187
|
+
reasoningProfile: "gpt-5.6",
|
|
188
|
+
supportsReasoningSummary: true
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: "gpt-5.6-terra",
|
|
192
|
+
name: "5.6 Terra",
|
|
193
|
+
contextWindow: 272e3,
|
|
194
|
+
inputModalities: ["text", "image"],
|
|
195
|
+
defaultReasoningEffort: "medium",
|
|
196
|
+
reasoningProfile: "gpt-5.6",
|
|
197
|
+
supportsReasoningSummary: true
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: "gpt-5.6-luna",
|
|
201
|
+
name: "5.6 Luna",
|
|
202
|
+
contextWindow: 272e3,
|
|
203
|
+
inputModalities: ["text", "image"],
|
|
204
|
+
defaultReasoningEffort: "medium",
|
|
205
|
+
reasoningProfile: "gpt-5.6",
|
|
206
|
+
supportsReasoningSummary: true
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "gpt-5.5",
|
|
210
|
+
name: "5.5",
|
|
211
|
+
contextWindow: 272e3,
|
|
212
|
+
inputModalities: ["text", "image"],
|
|
213
|
+
defaultReasoningEffort: "medium",
|
|
214
|
+
reasoningProfile: "standard",
|
|
215
|
+
supportsReasoningSummary: true
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "gpt-5.4",
|
|
219
|
+
name: "5.4",
|
|
220
|
+
contextWindow: 272e3,
|
|
221
|
+
inputModalities: ["text", "image"],
|
|
222
|
+
defaultReasoningEffort: "none",
|
|
223
|
+
reasoningProfile: "standard",
|
|
224
|
+
supportsReasoningSummary: true
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "gpt-5.4-mini",
|
|
228
|
+
name: "5.4 Mini",
|
|
229
|
+
contextWindow: 272e3,
|
|
230
|
+
inputModalities: ["text", "image"],
|
|
231
|
+
defaultReasoningEffort: "none",
|
|
232
|
+
reasoningProfile: "standard",
|
|
233
|
+
supportsReasoningSummary: true
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
id: "gpt-5.3-codex-spark",
|
|
237
|
+
name: "5.3 Codex Spark",
|
|
238
|
+
contextWindow: 258e3,
|
|
239
|
+
inputModalities: ["text"],
|
|
240
|
+
defaultReasoningEffort: "high",
|
|
241
|
+
reasoningProfile: "standard",
|
|
242
|
+
supportsReasoningSummary: false
|
|
243
|
+
}
|
|
244
|
+
];
|
|
245
|
+
CODEX_MODEL_CATALOG[0];
|
|
10
246
|
//#endregion
|
|
11
247
|
//#region src/client/api.ts
|
|
12
248
|
var SubscriptionApi = class {
|
|
@@ -31,6 +267,9 @@ window.__ModuleLoader__.load({
|
|
|
31
267
|
testConnection() {
|
|
32
268
|
return post(`${ROUTE_PREFIX}/connection/test`, {});
|
|
33
269
|
}
|
|
270
|
+
updatePreferences(patch) {
|
|
271
|
+
return post(`${ROUTE_PREFIX}/preferences/update`, patch);
|
|
272
|
+
}
|
|
34
273
|
events(loginId) {
|
|
35
274
|
return new EventSource(`${ROUTE_PREFIX}/login/events?loginId=${encodeURIComponent(loginId)}`);
|
|
36
275
|
}
|
|
@@ -61,15 +300,6 @@ window.__ModuleLoader__.load({
|
|
|
61
300
|
}
|
|
62
301
|
//#endregion
|
|
63
302
|
//#region src/client/CodexSubscriptionSection.tsx
|
|
64
|
-
const MODELS = [
|
|
65
|
-
"gpt-5.6-sol",
|
|
66
|
-
"gpt-5.6-terra",
|
|
67
|
-
"gpt-5.6-luna",
|
|
68
|
-
"gpt-5.5",
|
|
69
|
-
"gpt-5.4",
|
|
70
|
-
"gpt-5.4-mini",
|
|
71
|
-
"gpt-5.2"
|
|
72
|
-
];
|
|
73
303
|
function CodexSubscriptionSection({ t }) {
|
|
74
304
|
const apiRef = (0, react.useRef)(new SubscriptionApi());
|
|
75
305
|
const eventSourceRef = (0, react.useRef)(null);
|
|
@@ -182,6 +412,13 @@ window.__ModuleLoader__.load({
|
|
|
182
412
|
checkedAt: result.checkedAt
|
|
183
413
|
});
|
|
184
414
|
});
|
|
415
|
+
const updatePreferences = async (patch) => run("preferences", async () => {
|
|
416
|
+
const preferences = await apiRef.current.updatePreferences(patch);
|
|
417
|
+
setStatus((current) => current === null ? current : {
|
|
418
|
+
...current,
|
|
419
|
+
preferences
|
|
420
|
+
});
|
|
421
|
+
});
|
|
185
422
|
const logout = async () => run("logout", async () => {
|
|
186
423
|
await apiRef.current.logout();
|
|
187
424
|
eventSourceRef.current?.close();
|
|
@@ -310,7 +547,10 @@ window.__ModuleLoader__.load({
|
|
|
310
547
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
311
548
|
className: "dsh-codex-models",
|
|
312
549
|
"aria-label": t("models"),
|
|
313
|
-
children:
|
|
550
|
+
children: CODEX_MODEL_CATALOG.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
551
|
+
title: model.id,
|
|
552
|
+
children: model.name
|
|
553
|
+
}, model.id))
|
|
314
554
|
}),
|
|
315
555
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
316
556
|
className: "dsh-codex-actions",
|
|
@@ -322,6 +562,41 @@ window.__ModuleLoader__.load({
|
|
|
322
562
|
})
|
|
323
563
|
]
|
|
324
564
|
}),
|
|
565
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
|
|
566
|
+
title: t("enhancements"),
|
|
567
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
568
|
+
className: "dsh-codex-pref-row",
|
|
569
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("searchProvider") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
570
|
+
className: "dsh-codex-muted",
|
|
571
|
+
children: t("searchProviderHint")
|
|
572
|
+
})] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
573
|
+
className: "dsh-codex-segments",
|
|
574
|
+
role: "radiogroup",
|
|
575
|
+
"aria-label": t("searchProvider"),
|
|
576
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
577
|
+
type: "radio",
|
|
578
|
+
name: "dsh-codex-search-provider",
|
|
579
|
+
checked: status?.preferences.searchProvider === "dsh",
|
|
580
|
+
disabled: busy !== null,
|
|
581
|
+
onChange: () => updatePreferences({ searchProvider: "dsh" })
|
|
582
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("searchProviderDsh") })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
583
|
+
type: "radio",
|
|
584
|
+
name: "dsh-codex-search-provider",
|
|
585
|
+
checked: status?.preferences.searchProvider === "codex",
|
|
586
|
+
disabled: busy !== null,
|
|
587
|
+
onChange: () => updatePreferences({ searchProvider: "codex" })
|
|
588
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("searchProviderCodex") })] })]
|
|
589
|
+
})]
|
|
590
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
591
|
+
className: "dsh-codex-check",
|
|
592
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
593
|
+
type: "checkbox",
|
|
594
|
+
checked: status?.preferences.quickQuotaVisible === true,
|
|
595
|
+
disabled: busy !== null,
|
|
596
|
+
onChange: (event) => updatePreferences({ quickQuotaVisible: event.currentTarget.checked })
|
|
597
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("quickQuota") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: t("quickQuotaHint") })] })]
|
|
598
|
+
})]
|
|
599
|
+
}),
|
|
325
600
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Section, {
|
|
326
601
|
title: t("quota"),
|
|
327
602
|
aside: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
@@ -342,6 +617,23 @@ window.__ModuleLoader__.load({
|
|
|
342
617
|
bucket,
|
|
343
618
|
t
|
|
344
619
|
}, bucket.id)),
|
|
620
|
+
status?.quota.credits !== null && status?.quota.credits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaFact, {
|
|
621
|
+
label: t("credits"),
|
|
622
|
+
value: status.quota.credits.unlimited ? t("unlimited") : status.quota.credits.balance ?? (status.quota.credits.hasCredits ? t("available") : t("unavailable"))
|
|
623
|
+
}) : null,
|
|
624
|
+
status?.quota.individualLimit !== null && status?.quota.individualLimit !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaFact, {
|
|
625
|
+
label: t("monthlySpend"),
|
|
626
|
+
value: individualLimitLabel(status.quota.individualLimit, t)
|
|
627
|
+
}) : null,
|
|
628
|
+
status?.quota.resetCredits !== null && status?.quota.resetCredits !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaFact, {
|
|
629
|
+
label: t("resetCredits"),
|
|
630
|
+
value: String(status.quota.resetCredits.availableCount)
|
|
631
|
+
}) : null,
|
|
632
|
+
status?.quota.spendControlReached === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
633
|
+
className: "dsh-codex-warning",
|
|
634
|
+
role: "status",
|
|
635
|
+
children: t("spendControlReached")
|
|
636
|
+
}) : null,
|
|
345
637
|
status?.quota.state === "empty" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
346
638
|
className: "dsh-codex-empty",
|
|
347
639
|
children: t("noQuota")
|
|
@@ -420,24 +712,23 @@ window.__ModuleLoader__.load({
|
|
|
420
712
|
return t("securityUnavailable");
|
|
421
713
|
}
|
|
422
714
|
function QuotaBucket({ bucket, t }) {
|
|
715
|
+
const windows = quotaWindows(bucket);
|
|
423
716
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
|
|
424
717
|
className: "dsh-codex-quota-card",
|
|
425
|
-
children: [
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}) : null
|
|
440
|
-
]
|
|
718
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
719
|
+
className: "dsh-codex-quota-title",
|
|
720
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: bucket.name }), bucket.planType ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: bucket.planType }) : null]
|
|
721
|
+
}), windows.map((window, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaBar, {
|
|
722
|
+
label: windowLabel(window.windowDurationMins, t),
|
|
723
|
+
window,
|
|
724
|
+
t
|
|
725
|
+
}, `${window.windowDurationMins ?? "x"}:${window.resetsAt ?? "x"}:${index}`))]
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
function QuotaFact({ label, value }) {
|
|
729
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
730
|
+
className: "dsh-codex-quota-fact",
|
|
731
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: value })]
|
|
441
732
|
});
|
|
442
733
|
}
|
|
443
734
|
function QuotaBar({ label, window, t }) {
|
|
@@ -487,6 +778,15 @@ window.__ModuleLoader__.load({
|
|
|
487
778
|
function formatPercent(value) {
|
|
488
779
|
return `${new Intl.NumberFormat(void 0, { maximumFractionDigits: 1 }).format(value)}%`;
|
|
489
780
|
}
|
|
781
|
+
function individualLimitLabel(limit, t) {
|
|
782
|
+
const parts = [
|
|
783
|
+
limit.remainingPercent !== null ? `${formatPercent(limit.remainingPercent)} ${t("remaining")}` : null,
|
|
784
|
+
limit.limit !== null ? `${t("limit")}: ${limit.limit}` : null,
|
|
785
|
+
limit.used !== null ? `${t("used")}: ${limit.used}` : null,
|
|
786
|
+
limit.resetsAt !== null ? `${t("resets")}: ${formatReset(limit.resetsAt)}` : null
|
|
787
|
+
].filter((part) => part !== null);
|
|
788
|
+
return parts.length > 0 ? parts.join(" · ") : t("unknown");
|
|
789
|
+
}
|
|
490
790
|
function formatDate(seconds) {
|
|
491
791
|
if (seconds === void 0) return "—";
|
|
492
792
|
return new Intl.DateTimeFormat(void 0, {
|
|
@@ -544,6 +844,13 @@ window.__ModuleLoader__.load({
|
|
|
544
844
|
testing: "测试中…",
|
|
545
845
|
latency: "最近延迟",
|
|
546
846
|
models: "可用模型",
|
|
847
|
+
enhancements: "增强功能",
|
|
848
|
+
searchProvider: "搜索来源",
|
|
849
|
+
searchProviderHint: "选择 DSH Web 搜索工具使用默认来源,或改用当前 ChatGPT 订阅的 Codex 搜索来源。",
|
|
850
|
+
searchProviderDsh: "DSH 默认",
|
|
851
|
+
searchProviderCodex: "Codex 订阅",
|
|
852
|
+
quickQuota: "在输入框旁显示快捷用量",
|
|
853
|
+
quickQuotaHint: "仅在当前会话选中 codex-chatgpt 模型时显示。",
|
|
547
854
|
quota: "用量与限额",
|
|
548
855
|
quotaIntro: "数据来自 ChatGPT Codex 用量服务。页面可见时最多每 60 秒刷新一次。",
|
|
549
856
|
refreshQuota: "刷新用量",
|
|
@@ -557,8 +864,28 @@ window.__ModuleLoader__.load({
|
|
|
557
864
|
limitWindow: "额度",
|
|
558
865
|
used: "已使用",
|
|
559
866
|
remaining: "剩余",
|
|
867
|
+
available: "可用",
|
|
868
|
+
unavailable: "不可用",
|
|
869
|
+
unlimited: "无限",
|
|
870
|
+
credits: "Credits",
|
|
871
|
+
monthlySpend: "月度消费控制",
|
|
872
|
+
resetCredits: "重置次数",
|
|
873
|
+
spendControlReached: "已触发月度消费控制,新的订阅调用可能被限制。",
|
|
874
|
+
limit: "上限",
|
|
560
875
|
exhausted: "额度已用尽",
|
|
561
876
|
resets: "重置",
|
|
877
|
+
quickQuotaLabel: "Codex 剩余",
|
|
878
|
+
quickQuotaLoading: "用量…",
|
|
879
|
+
imageToolRunning: "正在生成图片",
|
|
880
|
+
imageToolDone: "已生成图片",
|
|
881
|
+
imageToolFailed: "图片生成失败",
|
|
882
|
+
image: "图片",
|
|
883
|
+
openImage: "打开图片",
|
|
884
|
+
openNamedImage: "打开 {name}",
|
|
885
|
+
imageLoading: "加载图片…",
|
|
886
|
+
imageLoadFailed: "图片加载失败,点击重试",
|
|
887
|
+
imagePreviewClose: "关闭",
|
|
888
|
+
imagePreviewOpenOriginal: "打开原图",
|
|
562
889
|
retry: "重试",
|
|
563
890
|
unknown: "未知"
|
|
564
891
|
},
|
|
@@ -598,6 +925,13 @@ window.__ModuleLoader__.load({
|
|
|
598
925
|
testing: "Testing…",
|
|
599
926
|
latency: "Last latency",
|
|
600
927
|
models: "Available models",
|
|
928
|
+
enhancements: "Enhancements",
|
|
929
|
+
searchProvider: "Search provider",
|
|
930
|
+
searchProviderHint: "Choose whether DSH web search uses its default provider or the Codex search source from this ChatGPT subscription.",
|
|
931
|
+
searchProviderDsh: "DSH default",
|
|
932
|
+
searchProviderCodex: "Codex subscription",
|
|
933
|
+
quickQuota: "Show quick usage beside the composer",
|
|
934
|
+
quickQuotaHint: "Shown only when the current session uses a codex-chatgpt model.",
|
|
601
935
|
quota: "Usage and limits",
|
|
602
936
|
quotaIntro: "Data comes from the ChatGPT Codex usage service and refreshes at most once per minute while visible.",
|
|
603
937
|
refreshQuota: "Refresh usage",
|
|
@@ -611,8 +945,28 @@ window.__ModuleLoader__.load({
|
|
|
611
945
|
limitWindow: "limit",
|
|
612
946
|
used: "used",
|
|
613
947
|
remaining: "remaining",
|
|
948
|
+
available: "Available",
|
|
949
|
+
unavailable: "Unavailable",
|
|
950
|
+
unlimited: "Unlimited",
|
|
951
|
+
credits: "Credits",
|
|
952
|
+
monthlySpend: "Monthly spend control",
|
|
953
|
+
resetCredits: "Reset credits",
|
|
954
|
+
spendControlReached: "Monthly spend control has been reached; new subscription calls may be limited.",
|
|
955
|
+
limit: "Limit",
|
|
614
956
|
exhausted: "Quota exhausted",
|
|
615
957
|
resets: "Resets",
|
|
958
|
+
quickQuotaLabel: "Codex left",
|
|
959
|
+
quickQuotaLoading: "Usage…",
|
|
960
|
+
imageToolRunning: "Generating image",
|
|
961
|
+
imageToolDone: "Generated image",
|
|
962
|
+
imageToolFailed: "Image generation failed",
|
|
963
|
+
image: "Image",
|
|
964
|
+
openImage: "Open image",
|
|
965
|
+
openNamedImage: "Open {name}",
|
|
966
|
+
imageLoading: "Loading image…",
|
|
967
|
+
imageLoadFailed: "Image failed to load. Click to retry",
|
|
968
|
+
imagePreviewClose: "Close",
|
|
969
|
+
imagePreviewOpenOriginal: "Open original",
|
|
616
970
|
retry: "Retry",
|
|
617
971
|
unknown: "Unknown"
|
|
618
972
|
}
|
|
@@ -645,9 +999,24 @@ window.__ModuleLoader__.load({
|
|
|
645
999
|
.dsh-codex-link{color:var(--dsw-alias-label-link,#3278d4);display:inline-block;font-size:13px;margin-top:8px}
|
|
646
1000
|
.dsh-codex-models{display:flex;flex-wrap:wrap;gap:6px;padding-top:12px}
|
|
647
1001
|
.dsh-codex-models code{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:5px;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px;padding:4px 6px}
|
|
1002
|
+
.dsh-codex-pref-row{align-items:center;border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;gap:16px;justify-content:space-between;min-height:58px;padding:10px 0}
|
|
1003
|
+
.dsh-codex-pref-row strong,.dsh-codex-check strong{display:block;font-size:13px;font-weight:600;line-height:1.35}
|
|
1004
|
+
.dsh-codex-segments{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex:none;gap:2px;padding:2px}
|
|
1005
|
+
.dsh-codex-segments label{cursor:pointer;display:block}
|
|
1006
|
+
.dsh-codex-segments input{position:absolute;opacity:0;pointer-events:none}
|
|
1007
|
+
.dsh-codex-segments span{border-radius:6px;color:var(--dsw-alias-label-secondary);display:block;font-size:12px;line-height:1;padding:7px 10px;white-space:nowrap}
|
|
1008
|
+
.dsh-codex-segments input:checked+span{background:var(--dsw-alias-bg-base);box-shadow:0 1px 2px rgba(0,0,0,.08);color:var(--dsw-alias-label-primary)}
|
|
1009
|
+
.dsh-codex-segments input:focus-visible+span{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
|
|
1010
|
+
.dsh-codex-segments input:disabled+span{cursor:default;opacity:.5}
|
|
1011
|
+
.dsh-codex-check{align-items:flex-start;border-bottom:1px solid var(--dsw-alias-border-l2);cursor:pointer;display:flex;gap:10px;padding:12px 0}
|
|
1012
|
+
.dsh-codex-check input{flex:none;margin-top:2px}
|
|
1013
|
+
.dsh-codex-check small{color:var(--dsw-alias-label-secondary);display:block;font-size:12px;line-height:1.45;margin-top:2px}
|
|
648
1014
|
.dsh-codex-quota-card{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;margin-top:12px;padding:12px}
|
|
649
1015
|
.dsh-codex-quota-title{align-items:center;display:flex;font-size:13px;gap:8px;justify-content:space-between}
|
|
650
1016
|
.dsh-codex-quota-title span{color:var(--dsw-alias-label-tertiary);font-size:11px;text-transform:uppercase}
|
|
1017
|
+
.dsh-codex-quota-fact{align-items:flex-start;border-bottom:1px solid var(--dsw-alias-border-l2);display:flex;font-size:12px;gap:12px;justify-content:space-between;line-height:1.45;padding:10px 0}
|
|
1018
|
+
.dsh-codex-quota-fact span{color:var(--dsw-alias-label-secondary);flex:none}
|
|
1019
|
+
.dsh-codex-quota-fact strong{font-weight:600;min-width:0;overflow-wrap:anywhere;text-align:right}
|
|
651
1020
|
.dsh-codex-meter-wrap{margin-top:13px}
|
|
652
1021
|
.dsh-codex-meter-label,.dsh-codex-meter-meta{display:flex;gap:10px;justify-content:space-between}
|
|
653
1022
|
.dsh-codex-meter-label{font-size:12px;margin-bottom:6px}
|
|
@@ -662,8 +1031,22 @@ window.__ModuleLoader__.load({
|
|
|
662
1031
|
.dsh-codex-skeleton span{animation:dsh-codex-pulse 1.4s ease-in-out infinite;background:var(--dsw-alias-bg-layer-2);border-radius:5px;height:42px}
|
|
663
1032
|
.dsh-codex-skeleton span:nth-child(2){animation-delay:.12s}.dsh-codex-skeleton span:nth-child(3){animation-delay:.24s}
|
|
664
1033
|
.dsh-codex-sr{height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;clip:rect(0 0 0 0);white-space:nowrap}
|
|
1034
|
+
.dsh-codex-composer-quota{align-items:center;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;color:var(--dsw-alias-label-secondary);display:inline-flex;font-size:11px;gap:6px;height:28px;line-height:1;max-width:160px;padding:0 9px;white-space:nowrap}
|
|
1035
|
+
.dsh-codex-composer-quota strong{color:var(--dsw-alias-label-primary);font-size:11px;font-weight:650}
|
|
1036
|
+
.dsh-codex-composer-quota[data-level=warning] strong{color:var(--dsw-alias-label-warning,#d58a24)}
|
|
1037
|
+
.dsh-codex-composer-quota[data-level=danger] strong{color:var(--dsw-alias-label-danger,#d94b4b)}
|
|
1038
|
+
.dsh-codex-image-tool{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:flex;flex-direction:column;gap:8px;margin:4px 0 4px 4px;max-width:520px;padding:10px 12px}
|
|
1039
|
+
.dsh-codex-image-tool-head{align-items:center;display:flex;font-size:13px;gap:8px;min-width:0}
|
|
1040
|
+
.dsh-codex-image-dot{background:var(--dsw-alias-button-info-fill,#397ee8);border-radius:50%;display:inline-block;flex:none;height:8px;width:8px}
|
|
1041
|
+
.dsh-codex-image-tool[data-state=error] .dsh-codex-image-dot{background:var(--dsw-alias-label-danger,#d94b4b)}
|
|
1042
|
+
.dsh-codex-image-title{font-weight:600}
|
|
1043
|
+
.dsh-codex-image-summary{color:var(--dsw-alias-label-tertiary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1044
|
+
.dsh-codex-image-grid{display:flex;flex-wrap:wrap;gap:8px}
|
|
1045
|
+
.dsh-codex-image-preview{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;display:block;max-height:260px;max-width:min(100%,360px);object-fit:contain}
|
|
1046
|
+
.dsh-codex-image-loading,.dsh-codex-image-failed{border:1px dashed var(--dsw-alias-border-l2);border-radius:8px;color:var(--dsw-alias-label-tertiary);display:inline-flex;font-size:12px;line-height:1.4;padding:18px 20px}
|
|
1047
|
+
.dsh-codex-image-failed{color:var(--dsw-alias-label-danger,#d94b4b)}
|
|
665
1048
|
@keyframes dsh-codex-pulse{0%,100%{opacity:.55}50%{opacity:1}}
|
|
666
|
-
@media(max-width:560px){.dsh-codex-row{align-items:flex-start;flex-direction:column;gap:3px}.dsh-codex-value{text-align:left}.dsh-codex-actions{justify-content:flex-start}.dsh-codex-grouphead{align-items:flex-start;flex-direction:column;gap:0;padding:12px 0}.dsh-codex-meter-meta{align-items:flex-start;flex-direction:column;gap:2px}.dsh-codex-errorbar{align-items:flex-start;flex-direction:column}}
|
|
1049
|
+
@media(max-width:560px){.dsh-codex-row,.dsh-codex-pref-row,.dsh-codex-quota-fact{align-items:flex-start;flex-direction:column;gap:3px}.dsh-codex-value,.dsh-codex-quota-fact strong{text-align:left}.dsh-codex-actions{justify-content:flex-start}.dsh-codex-grouphead{align-items:flex-start;flex-direction:column;gap:0;padding:12px 0}.dsh-codex-meter-meta{align-items:flex-start;flex-direction:column;gap:2px}.dsh-codex-errorbar{align-items:flex-start;flex-direction:column}.dsh-codex-segments{width:100%}.dsh-codex-segments label{flex:1}.dsh-codex-segments span{text-align:center}.dsh-codex-image-tool{max-width:100%;margin-left:0}}
|
|
667
1050
|
@media(prefers-reduced-motion:reduce){.dsh-codex-meter>span{transition:none}.dsh-codex-skeleton span{animation:none}}
|
|
668
1051
|
`;
|
|
669
1052
|
function installStyles() {
|
|
@@ -677,7 +1060,12 @@ window.__ModuleLoader__.load({
|
|
|
677
1060
|
}
|
|
678
1061
|
//#endregion
|
|
679
1062
|
//#region src/client/index.tsx
|
|
680
|
-
const inject = [
|
|
1063
|
+
const inject = [
|
|
1064
|
+
"slots",
|
|
1065
|
+
"locale",
|
|
1066
|
+
"modelDirectories",
|
|
1067
|
+
"conversation"
|
|
1068
|
+
];
|
|
681
1069
|
function apply(ctx) {
|
|
682
1070
|
ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-chatgpt-subscription: dictionaries");
|
|
683
1071
|
ctx.effect(() => installStyles(), "dsh-chatgpt-subscription: styles");
|
|
@@ -688,6 +1076,32 @@ window.__ModuleLoader__.load({
|
|
|
688
1076
|
label: "Codex 订阅",
|
|
689
1077
|
locale: NS
|
|
690
1078
|
}, CodexSubscriptionSection));
|
|
1079
|
+
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|
|
1080
|
+
name: "conversation.input.right",
|
|
1081
|
+
id: "codex-subscription-quota",
|
|
1082
|
+
order: 35,
|
|
1083
|
+
locale: NS,
|
|
1084
|
+
inject: (sessionId) => {
|
|
1085
|
+
const directory = ctx.modelDirectories.directoryFor(sessionId);
|
|
1086
|
+
return {
|
|
1087
|
+
api: new SubscriptionApi(),
|
|
1088
|
+
directory: directory.store,
|
|
1089
|
+
loadModelDirectory: () => {
|
|
1090
|
+
directory.load().catch(() => void 0);
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
}, CodexComposerQuota));
|
|
1095
|
+
ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
|
|
1096
|
+
name: "tool.call.toolview",
|
|
1097
|
+
key: CODEX_IMAGE_TOOL_NAME,
|
|
1098
|
+
locale: NS,
|
|
1099
|
+
inject: (sessionId) => ({ loadImage: imageLoader(ctx, sessionId) })
|
|
1100
|
+
}, CodexImageToolView));
|
|
1101
|
+
}
|
|
1102
|
+
function imageLoader(ctx, sessionId) {
|
|
1103
|
+
const conversation = ctx.conversation;
|
|
1104
|
+
return (attachment) => conversation.resolveImage(sessionId, attachment);
|
|
691
1105
|
}
|
|
692
1106
|
//#endregion
|
|
693
1107
|
exports.apply = apply;
|