dsh-codex-subscription 2.2.5 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +16 -2
- package/README.md +16 -2
- package/lib/client.js +16 -3
- package/lib/index.js +74 -8
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -31,7 +31,7 @@ Subscription access, model controls, quota management, and image creation in one
|
|
|
31
31
|
| Feature | What you can do | Availability / setup |
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| **Subscription sign-in** | Sign in to ChatGPT and use Codex subscription models without an API key | Normal chat needs no Codex CLI |
|
|
34
|
-
| **Model catalog sync** | Read available models and reasoning levels from your account, with manual refresh | New models appear when your account offers them |
|
|
34
|
+
| **Model catalog sync** | Read available models and reasoning levels from your account, with manual refresh; models marked for retirement show their retirement date next to the model name, without automatically switching your selection | New models appear when your account offers them |
|
|
35
35
|
| **Reasoning levels** | Choose supported reasoning effort in the composer | Levels vary by model |
|
|
36
36
|
| **Fast mode** | Switch supported models between Standard and Fast, with a lightning indicator | Standard by default; Fast increases usage |
|
|
37
37
|
| **Response verbosity** | Request short, medium, or detailed responses | Follows the model default unless changed |
|
|
@@ -87,7 +87,7 @@ Subscription access, model controls, quota management, and image creation in one
|
|
|
87
87
|
|
|
88
88
|
## Prepare DSH
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
Verified against DSH `0.2.0-rc.1` (`next` channel) and `0.1.7-rc.2` (`latest` channel), with earlier accepted versions still supported.
|
|
91
91
|
|
|
92
92
|
This plugin supports the latest DeepSeek Harness release recorded in its package metadata and requires a ChatGPT account that currently has Codex access.
|
|
93
93
|
|
|
@@ -270,6 +270,13 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
270
270
|
|
|
271
271
|
## Troubleshooting
|
|
272
272
|
|
|
273
|
+
<details>
|
|
274
|
+
<summary>DSH 0.2.0-rc.1 reports plugin 2.2.4 as incompatible?</summary>
|
|
275
|
+
|
|
276
|
+
DSH 0.2.0-rc.1 did not exist when 2.2.4 and earlier were published, and a published package's compatibility declaration cannot be changed. Update to 2.2.5 or later from the **Plugins** page, or run `dsh plugin --profile web update dsh-codex-subscription`, then restart DSH.
|
|
277
|
+
|
|
278
|
+
</details>
|
|
279
|
+
|
|
273
280
|
<details>
|
|
274
281
|
<summary>No dsh command on your computer?</summary>
|
|
275
282
|
|
|
@@ -303,6 +310,13 @@ The current plugin icon and its appearance in DSH’s Installed list.
|
|
|
303
310
|
|
|
304
311
|
<sub>Captured in DSH 0.1.7-alpha.2.</sub>
|
|
305
312
|
|
|
313
|
+
## Positioning and boundaries
|
|
314
|
+
|
|
315
|
+
- **ChatGPT / Codex subscription only**, done in depth: quota and forecasting, images and sketches, subtasks, diagnostics. Other subscriptions such as Claude or Grok are out of scope.
|
|
316
|
+
- **Fail explicitly**: requests never silently fall back to another paid route, and quota windows the account does not return are never invented.
|
|
317
|
+
- **Tracks official DSH releases**: every release is accepted end to end (install, start, remove, reinstall) against official DSH channels such as latest and next; verified versions are recorded in the [compatibility notes](docs/dsh-020-compatibility.md).
|
|
318
|
+
- Models, quota and features depend on what your account actually returns; the ChatGPT backend and DSH can change independently.
|
|
319
|
+
|
|
306
320
|
## Scope and support
|
|
307
321
|
|
|
308
322
|
The ChatGPT Codex backend and DSH can change independently. This community project is not affiliated with or endorsed by DeepSeek or OpenAI.
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
| 功能 | 你可以做什么 | 使用方式 / 条件 |
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| **订阅登录** | 直接登录 ChatGPT,使用 Codex 订阅模型,无需 API Key | 普通聊天无需 Codex CLI |
|
|
34
|
-
| **模型目录同步** |
|
|
34
|
+
| **模型目录同步** | 自动读取账号可用模型与推理档位,支持手动刷新;目录标注退役的模型会在模型名称旁显示退役日期,不会自动切换你的选择 | 新型号以账号目录开放情况为准 |
|
|
35
35
|
| **推理等级** | 在输入框选择模型支持的推理档位 | 档位随模型变化 |
|
|
36
36
|
| **高速模式** | 在支持的模型上切换标准 / Fast,输入框显示闪电标识 | 默认标准;Fast 会增加用量消耗 |
|
|
37
37
|
| **输出详略** | 选择简短、适中或详细的回答偏好 | 默认跟随模型 |
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
|
|
88
88
|
## 准备 DSH
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
已验证兼容 DSH `0.2.0-rc.1`(`next` 通道)和 `0.1.7-rc.2`(`latest` 通道),并继续支持更早的已验收版本。
|
|
91
91
|
|
|
92
92
|
本插件支持软件包元数据中记录的最新版 DeepSeek Harness,并需要一个当前具有 Codex 使用资格的 ChatGPT 账户。
|
|
93
93
|
|
|
@@ -270,6 +270,13 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
270
270
|
|
|
271
271
|
## 常见问题
|
|
272
272
|
|
|
273
|
+
<details>
|
|
274
|
+
<summary>DSH 0.2.0-rc.1 提示插件 2.2.4 不兼容?</summary>
|
|
275
|
+
|
|
276
|
+
2.2.4 及更早版本发布时 DSH 0.2.0-rc.1 还不存在,其兼容声明无法事后修改。请更新到 2.2.5 或更高版本:在 **插件** 页面更新,或运行 `dsh plugin --profile web update dsh-codex-subscription`,然后重启 DSH。
|
|
277
|
+
|
|
278
|
+
</details>
|
|
279
|
+
|
|
273
280
|
<details>
|
|
274
281
|
<summary>电脑没有 dsh 命令,怎么安装?</summary>
|
|
275
282
|
|
|
@@ -303,6 +310,13 @@ dsh plugin --profile web remove dsh-codex-subscription
|
|
|
303
310
|
|
|
304
311
|
<sub>DSH 0.1.7-alpha.2 实机界面。</sub>
|
|
305
312
|
|
|
313
|
+
## 定位与边界
|
|
314
|
+
|
|
315
|
+
- **只做 ChatGPT / Codex 订阅**,并把这一件事做深:额度与预测、图片与草图、子任务、诊断;不接入 Claude、Grok 等其他订阅。
|
|
316
|
+
- **失败就明确报错**:不会静默切换到其他付费路由,也不会虚构账号没有返回的额度窗口。
|
|
317
|
+
- **跟随官方 DSH 发布**:每次发版都用官方 DSH 的 latest、next 等通道做安装、启动、卸载、重装的端到端验收;已验证的版本见 [兼容性记录](docs/dsh-020-compatibility.md)。
|
|
318
|
+
- 模型、额度和功能开放情况以你的账号实际返回为准;ChatGPT 后端与 DSH 可能独立变化。
|
|
319
|
+
|
|
306
320
|
## 边界与支持
|
|
307
321
|
|
|
308
322
|
ChatGPT Codex 后端和 DSH 可能独立变化;本项目为社区项目,与 DeepSeek、OpenAI 无隶属或背书关系。
|
package/lib/client.js
CHANGED
|
@@ -734,7 +734,20 @@ window.__ModuleLoader__.load({
|
|
|
734
734
|
//#endregion
|
|
735
735
|
//#region src/image-conversation-node.js
|
|
736
736
|
const KIND = "codex-image-output";
|
|
737
|
-
const
|
|
737
|
+
const hasImage = (block) => block?.content?.some((part) => part.type === "image" && part.attachment);
|
|
738
|
+
const isImage = (part) => part?.type === "image" && part.attachment;
|
|
739
|
+
const resultsOf = (event) => {
|
|
740
|
+
if (event?.type !== "tool/result" || event.data.meta?.kind !== "codex-subscription-image") return [];
|
|
741
|
+
const content = event.data.message?.content ?? [];
|
|
742
|
+
const wrapped = content.filter((block) => block.type === "tool-result" && !block.isError && hasImage(block));
|
|
743
|
+
if (wrapped.length > 0) return wrapped;
|
|
744
|
+
return content.some(isImage) ? [{
|
|
745
|
+
type: "tool-result",
|
|
746
|
+
toolCallId: `${event.data.turn}:${event.data.step ?? event.seq}`,
|
|
747
|
+
isError: false,
|
|
748
|
+
content
|
|
749
|
+
}] : [];
|
|
750
|
+
};
|
|
738
751
|
const imageConversationNode = {
|
|
739
752
|
kind: KIND,
|
|
740
753
|
target: "chat",
|
|
@@ -1110,7 +1123,7 @@ window.__ModuleLoader__.load({
|
|
|
1110
1123
|
})
|
|
1111
1124
|
]
|
|
1112
1125
|
}),
|
|
1113
|
-
!showOutput || image === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1126
|
+
!showOutput && !settled || image === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1114
1127
|
className: "codexImageToolGallery",
|
|
1115
1128
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CodexGeneratedImage, {
|
|
1116
1129
|
features,
|
|
@@ -7623,7 +7636,7 @@ window.__ModuleLoader__.load({
|
|
|
7623
7636
|
}
|
|
7624
7637
|
//#endregion
|
|
7625
7638
|
//#region src/version.js
|
|
7626
|
-
const PACKAGE_VERSION = "2.2.
|
|
7639
|
+
const PACKAGE_VERSION = "2.2.6";
|
|
7627
7640
|
//#endregion
|
|
7628
7641
|
//#region src/client-recovery.js
|
|
7629
7642
|
async function recoveryCall(rpc, endpoint, payload = {}, timeoutMs = 1e4) {
|
package/lib/index.js
CHANGED
|
@@ -3010,7 +3010,7 @@ async function readSubscriptionCredentials(getAuth, readCredential, signal) {
|
|
|
3010
3010
|
}
|
|
3011
3011
|
//#endregion
|
|
3012
3012
|
//#region src/version.js
|
|
3013
|
-
const PACKAGE_VERSION = "2.2.
|
|
3013
|
+
const PACKAGE_VERSION = "2.2.6";
|
|
3014
3014
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
3015
3015
|
//#endregion
|
|
3016
3016
|
//#region src/model-catalog.js
|
|
@@ -3028,6 +3028,43 @@ const DEFAULT_REFRESH_TIMEOUT_MS = 1e4;
|
|
|
3028
3028
|
const record$4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3029
3029
|
const nonEmpty$2 = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
3030
3030
|
const positiveInteger$1 = (value) => Number.isSafeInteger(value) && value > 0 ? value : void 0;
|
|
3031
|
+
function upgradeModelId(value) {
|
|
3032
|
+
return (typeof value.upgrade === "string" ? nonEmpty$2(value.upgrade) : record$4(value.upgrade) ? nonEmpty$2(value.upgrade.model) : void 0) ?? (record$4(value.upgrade_info) ? nonEmpty$2(value.upgrade_info.model) : void 0) ?? (record$4(value.upgradeInfo) ? nonEmpty$2(value.upgradeInfo.model) : void 0);
|
|
3033
|
+
}
|
|
3034
|
+
function retirementMillis(value) {
|
|
3035
|
+
for (const source of [
|
|
3036
|
+
value,
|
|
3037
|
+
value.upgrade,
|
|
3038
|
+
value.upgrade_info,
|
|
3039
|
+
value.upgradeInfo
|
|
3040
|
+
]) {
|
|
3041
|
+
if (!record$4(source)) continue;
|
|
3042
|
+
for (const raw of [source.retirement_at, source.retirementAt]) {
|
|
3043
|
+
const seconds = positiveInteger$1(raw);
|
|
3044
|
+
if (seconds !== void 0 && Number.isSafeInteger(seconds * 1e3)) return seconds * 1e3;
|
|
3045
|
+
if (typeof raw === "string" && /^\d{4}-\d{2}-\d{2}T[\d:.]+(?:Z|[+-]\d{2}:\d{2})$/u.test(raw)) {
|
|
3046
|
+
const parsed = Date.parse(raw);
|
|
3047
|
+
if (Number.isFinite(parsed) && parsed > 0) return parsed;
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
function retirementNotice(value, now = Date.now(), targetDisplayName) {
|
|
3053
|
+
if (!record$4(value) || !Number.isFinite(now)) return void 0;
|
|
3054
|
+
const at = retirementMillis(value);
|
|
3055
|
+
if (at === void 0 || at <= now) return void 0;
|
|
3056
|
+
const date = new Date(at);
|
|
3057
|
+
if (!Number.isFinite(date.getTime())) return void 0;
|
|
3058
|
+
const dateText = `${String(date.getUTCFullYear()).padStart(4, "0")}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}`;
|
|
3059
|
+
const upgradeTo = upgradeModelId(value);
|
|
3060
|
+
const upgradeName = upgradeTo === void 0 ? void 0 : nonEmpty$2(targetDisplayName) ?? upgradeTo;
|
|
3061
|
+
return {
|
|
3062
|
+
text: `Retires on ${dateText} (UTC).${upgradeName === void 0 ? "" : ` Switch to ${upgradeName} to keep working.`}`,
|
|
3063
|
+
at,
|
|
3064
|
+
upgradeTo,
|
|
3065
|
+
dateText
|
|
3066
|
+
};
|
|
3067
|
+
}
|
|
3031
3068
|
function reasoningMap(levels) {
|
|
3032
3069
|
const supported = new Set((Array.isArray(levels) ? levels : []).map((level) => nonEmpty$2(record$4(level) ? level.effort : void 0)).filter(Boolean));
|
|
3033
3070
|
const map = Object.fromEntries(LEVELS.map((level) => [level, null]));
|
|
@@ -3052,7 +3089,7 @@ function unsupportedCapabilities(value) {
|
|
|
3052
3089
|
...speeds.length ? { speeds } : {}
|
|
3053
3090
|
};
|
|
3054
3091
|
}
|
|
3055
|
-
function visibleModel(value) {
|
|
3092
|
+
function visibleModel(value, displayNames, now) {
|
|
3056
3093
|
if (!record$4(value)) return void 0;
|
|
3057
3094
|
const id = nonEmpty$2(value.slug);
|
|
3058
3095
|
const reserve = id === "gpt-reserve" && ["list", "hide"].includes(value.visibility);
|
|
@@ -3063,11 +3100,19 @@ function visibleModel(value) {
|
|
|
3063
3100
|
...value,
|
|
3064
3101
|
supported_reasoning_levels: supported
|
|
3065
3102
|
});
|
|
3103
|
+
const upgradeTo = upgradeModelId(value);
|
|
3104
|
+
const retirement = retirementNotice(value, now, displayNames.get(upgradeTo));
|
|
3105
|
+
const notice = retirement?.text;
|
|
3106
|
+
const description = nonEmpty$2(value.description);
|
|
3066
3107
|
return {
|
|
3067
3108
|
...Object.keys(unsupported).length ? { unsupported } : {},
|
|
3068
3109
|
id,
|
|
3069
|
-
name: reserve ? "GPT-Reserve (Experimental)" : nonEmpty$2(value.display_name) ?? id
|
|
3070
|
-
description:
|
|
3110
|
+
name: `${reserve ? "GPT-Reserve (Experimental)" : nonEmpty$2(value.display_name) ?? id}${retirement === void 0 ? "" : ` (retires ${retirement.dateText})`}`,
|
|
3111
|
+
description: notice === void 0 ? description : description === void 0 ? notice : `${description}\n${notice}`,
|
|
3112
|
+
...retirement === void 0 ? {} : { retirement: {
|
|
3113
|
+
at: retirement.at,
|
|
3114
|
+
upgradeTo: retirement.upgradeTo
|
|
3115
|
+
} },
|
|
3071
3116
|
priority: Number.isFinite(value.priority) ? value.priority : 0,
|
|
3072
3117
|
input: input.length > 0 ? input : ["text"],
|
|
3073
3118
|
contextWindow: positiveInteger$1(value.context_window) ?? positiveInteger$1(value.max_context_window),
|
|
@@ -3083,10 +3128,17 @@ function visibleModel(value) {
|
|
|
3083
3128
|
supportsFast: [...Array.isArray(value.additional_speed_tiers) ? value.additional_speed_tiers : [], ...Array.isArray(value.service_tiers) ? value.service_tiers.map((tier) => tier?.id) : []].some((tier) => tier === "fast" || tier === "priority")
|
|
3084
3129
|
};
|
|
3085
3130
|
}
|
|
3086
|
-
function parseOfficialModelCatalog(value) {
|
|
3131
|
+
function parseOfficialModelCatalog(value, now = Date.now()) {
|
|
3087
3132
|
if (!record$4(value) || !Array.isArray(value.models)) throw new Error("Codex returned a malformed model catalog");
|
|
3133
|
+
const displayNames = /* @__PURE__ */ new Map();
|
|
3134
|
+
for (const model of value.models) {
|
|
3135
|
+
if (!record$4(model)) continue;
|
|
3136
|
+
const id = nonEmpty$2(model.slug);
|
|
3137
|
+
const name = nonEmpty$2(model.display_name);
|
|
3138
|
+
if (id !== void 0 && name !== void 0 && !displayNames.has(id)) displayNames.set(id, name);
|
|
3139
|
+
}
|
|
3088
3140
|
const seen = /* @__PURE__ */ new Set();
|
|
3089
|
-
return value.models.map(visibleModel).filter((model) => model !== void 0 && !seen.has(model.id) && seen.add(model.id)).sort((left, right) => Number(left.id === "gpt-reserve") - Number(right.id === "gpt-reserve") || right.priority - left.priority);
|
|
3141
|
+
return value.models.map((model) => visibleModel(model, displayNames, now)).filter((model) => model !== void 0 && !seen.has(model.id) && seen.add(model.id)).sort((left, right) => Number(left.id === "gpt-reserve") - Number(right.id === "gpt-reserve") || right.priority - left.priority);
|
|
3090
3142
|
}
|
|
3091
3143
|
function mergeModel(baseModels, remote) {
|
|
3092
3144
|
const base = baseModels.find((model) => model.id === remote.id) ?? baseModels.find((model) => model.id !== "gpt-5.3-codex-spark") ?? baseModels[0];
|
|
@@ -3095,6 +3147,7 @@ function mergeModel(baseModels, remote) {
|
|
|
3095
3147
|
...base,
|
|
3096
3148
|
id: remote.id,
|
|
3097
3149
|
name: remote.name,
|
|
3150
|
+
...remote.retirement === void 0 || remote.description === void 0 ? {} : { description: remote.description },
|
|
3098
3151
|
input: remote.input,
|
|
3099
3152
|
reasoning: remote.reasoning,
|
|
3100
3153
|
thinkingLevelMap: remote.thinkingLevelMap,
|
|
@@ -3110,12 +3163,14 @@ function mergeModel(baseModels, remote) {
|
|
|
3110
3163
|
}
|
|
3111
3164
|
function createOfficialModelCatalog(options = {}) {
|
|
3112
3165
|
const fetchCatalog = options.fetch ?? fetch;
|
|
3166
|
+
const getNow = options.now ?? Date.now;
|
|
3113
3167
|
const scheduleTimeout = options.setTimeout ?? setTimeout;
|
|
3114
3168
|
const cancelTimeout = options.clearTimeout ?? clearTimeout;
|
|
3115
3169
|
const timeoutMs = Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : DEFAULT_REFRESH_TIMEOUT_MS;
|
|
3116
3170
|
let models;
|
|
3117
3171
|
let metadata = /* @__PURE__ */ new Map();
|
|
3118
3172
|
let etag;
|
|
3173
|
+
let rawCatalog;
|
|
3119
3174
|
let revision = 0;
|
|
3120
3175
|
let refreshing;
|
|
3121
3176
|
let generation = 0;
|
|
@@ -3155,10 +3210,19 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
3155
3210
|
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
3156
3211
|
if (response.status === 304) {
|
|
3157
3212
|
outcome = "ok";
|
|
3158
|
-
return false;
|
|
3213
|
+
if (rawCatalog === void 0) return false;
|
|
3214
|
+
const remote = parseOfficialModelCatalog(rawCatalog, getNow());
|
|
3215
|
+
const next = remote.map((model) => mergeModel(options.baseModels(), model)).filter(Boolean);
|
|
3216
|
+
if (next.length === 0 || JSON.stringify(next) === JSON.stringify(models)) return false;
|
|
3217
|
+
models = next;
|
|
3218
|
+
metadata = new Map(remote.map((model) => [model.id, model]));
|
|
3219
|
+
revision += 1;
|
|
3220
|
+
options.onUpdated?.();
|
|
3221
|
+
return true;
|
|
3159
3222
|
}
|
|
3160
3223
|
if (!response.ok) throw new Error(`Codex model catalog failed (HTTP ${response.status})`);
|
|
3161
|
-
const
|
|
3224
|
+
const raw = await response.json();
|
|
3225
|
+
const remote = parseOfficialModelCatalog(raw, getNow());
|
|
3162
3226
|
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
3163
3227
|
if (remote.length === 0) throw new Error("Codex returned an empty model catalog");
|
|
3164
3228
|
const baseModels = options.baseModels();
|
|
@@ -3166,6 +3230,7 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
3166
3230
|
if (next.length === 0) throw new Error("Codex model catalog has no compatible models");
|
|
3167
3231
|
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
3168
3232
|
models = next;
|
|
3233
|
+
rawCatalog = raw;
|
|
3169
3234
|
metadata = new Map(remote.map((model) => [model.id, model]));
|
|
3170
3235
|
etag = nonEmpty$2(response.headers.get("etag")) ?? etag;
|
|
3171
3236
|
revision += 1;
|
|
@@ -3219,6 +3284,7 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
3219
3284
|
refreshing = void 0;
|
|
3220
3285
|
flight?.cancel();
|
|
3221
3286
|
models = void 0;
|
|
3287
|
+
rawCatalog = void 0;
|
|
3222
3288
|
metadata = /* @__PURE__ */ new Map();
|
|
3223
3289
|
etag = void 0;
|
|
3224
3290
|
refreshStatus = "idle";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.6",
|
|
4
4
|
"description": "Use ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|