@tnnevol/dsh-codex-auth 0.1.7-rc.2 → 0.1.7-rc.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/compatibility.json +1 -0
- package/cordis.patch.yml +103 -10
- package/lib/client.js +578 -116
- package/lib/index.js +241 -8
- package/lib/style.css +1 -1
- package/package.json +5 -2
package/lib/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
|
9
9
|
/** Read-only Codex account quota information for the local settings card. */
|
|
10
10
|
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
11
11
|
const CODEX_USAGE_TIMEOUT_MS = 1e4;
|
|
12
|
-
function record$
|
|
12
|
+
function record$2(value) {
|
|
13
13
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
14
14
|
}
|
|
15
15
|
function number$1(value) {
|
|
@@ -29,7 +29,7 @@ function percentage(value) {
|
|
|
29
29
|
return Math.max(0, Math.min(100, value));
|
|
30
30
|
}
|
|
31
31
|
function window(value) {
|
|
32
|
-
const source = record$
|
|
32
|
+
const source = record$2(value);
|
|
33
33
|
if (source === void 0) return void 0;
|
|
34
34
|
const result = {};
|
|
35
35
|
const usedPercent = number$1(source["used_percent"]);
|
|
@@ -45,7 +45,7 @@ function window(value) {
|
|
|
45
45
|
return Object.keys(result).length > 0 ? result : void 0;
|
|
46
46
|
}
|
|
47
47
|
function credits(value) {
|
|
48
|
-
const source = record$
|
|
48
|
+
const source = record$2(value);
|
|
49
49
|
if (source === void 0) return void 0;
|
|
50
50
|
const result = {};
|
|
51
51
|
const hasCredits = boolean(source["has_credits"]);
|
|
@@ -58,9 +58,9 @@ function credits(value) {
|
|
|
58
58
|
}
|
|
59
59
|
/** Normalize the evolving private WHAM response into a small UI-safe shape. */
|
|
60
60
|
function normalizeCodexUsagePayload(value) {
|
|
61
|
-
const source = record$
|
|
61
|
+
const source = record$2(value);
|
|
62
62
|
if (source === void 0) throw new Error("Codex usage response was not an object");
|
|
63
|
-
const rateLimit = record$
|
|
63
|
+
const rateLimit = record$2(source["rate_limit"]);
|
|
64
64
|
const result = {};
|
|
65
65
|
const planType = string$1(source["plan_type"]);
|
|
66
66
|
const allowed = boolean(rateLimit?.["allowed"]);
|
|
@@ -171,14 +171,14 @@ const LLM_PI_AI_THINKING_LEVELS = /* @__PURE__ */ new Set([
|
|
|
171
171
|
]);
|
|
172
172
|
/** Rows the account endpoint returns for machinery, not human selection. */
|
|
173
173
|
const INTERNAL_CODEX_MODEL_IDS = /* @__PURE__ */ new Set(["codex-auto-review", "gpt-reserve"]);
|
|
174
|
-
function record(value) {
|
|
174
|
+
function record$1(value) {
|
|
175
175
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
176
176
|
}
|
|
177
177
|
function accessToken(auth) {
|
|
178
178
|
return typeof auth?.apiKey === "string" && auth.apiKey.length > 0 ? auth.apiKey : void 0;
|
|
179
179
|
}
|
|
180
180
|
function accountId(credential) {
|
|
181
|
-
const value = record(credential)?.accountId;
|
|
181
|
+
const value = record$1(credential)?.accountId;
|
|
182
182
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
183
183
|
}
|
|
184
184
|
function string(value) {
|
|
@@ -310,7 +310,7 @@ async function fetchCodexModelCatalog(store, fetchImpl = fetch) {
|
|
|
310
310
|
signal: controller.signal
|
|
311
311
|
});
|
|
312
312
|
if (!response.ok) throw new Error(`Codex model catalog request failed with status ${String(response.status)}`);
|
|
313
|
-
const parsed = record(await response.json());
|
|
313
|
+
const parsed = record$1(await response.json());
|
|
314
314
|
if (parsed === void 0 || !Array.isArray(parsed.models)) throw new Error("Codex model catalog response was not an object with a models array");
|
|
315
315
|
return { models: parsed.models };
|
|
316
316
|
} finally {
|
|
@@ -820,6 +820,205 @@ var CodexCredentialMirror = class {
|
|
|
820
820
|
}
|
|
821
821
|
};
|
|
822
822
|
//#endregion
|
|
823
|
+
//#region src/contracts/model-capabilities.ts
|
|
824
|
+
/** 深思等级的共同前缀:`minimal..max`,各模型的真实线路值。 */
|
|
825
|
+
const FULL_LEVELS = {
|
|
826
|
+
minimal: "low",
|
|
827
|
+
low: "low",
|
|
828
|
+
medium: "medium",
|
|
829
|
+
high: "high",
|
|
830
|
+
xhigh: "xhigh",
|
|
831
|
+
max: "max"
|
|
832
|
+
};
|
|
833
|
+
const CONTEXT_WINDOW = 272e3;
|
|
834
|
+
const MAX_TOKENS = 128e3;
|
|
835
|
+
/** 七个模型当前都是图文输入;单独抽出来避免每行重复书写造成漂移。 */
|
|
836
|
+
const TEXT_AND_IMAGE = ["text", "image"];
|
|
837
|
+
/**
|
|
838
|
+
* Codex Auth 声明并保证的模型集合,顺序即补丁中的书写顺序。
|
|
839
|
+
*
|
|
840
|
+
* `gpt-6-astra` 的目录条目把 `off` 映射为 `null`,即该模型确实没有 `off`;
|
|
841
|
+
* `gpt-5.5` 的目录不提供 `max`,因此它的等级到 `xhigh` 为止。
|
|
842
|
+
*/
|
|
843
|
+
const CODEX_MODEL_CAPABILITIES = [
|
|
844
|
+
{
|
|
845
|
+
id: "gpt-6-astra",
|
|
846
|
+
name: "GPT-6-Astra",
|
|
847
|
+
contextWindow: CONTEXT_WINDOW,
|
|
848
|
+
maxTokens: MAX_TOKENS,
|
|
849
|
+
input: TEXT_AND_IMAGE,
|
|
850
|
+
reasoningEfforts: { ...FULL_LEVELS }
|
|
851
|
+
},
|
|
852
|
+
{
|
|
853
|
+
id: "gpt-6-sol",
|
|
854
|
+
name: "GPT-6-Sol",
|
|
855
|
+
contextWindow: CONTEXT_WINDOW,
|
|
856
|
+
maxTokens: MAX_TOKENS,
|
|
857
|
+
input: TEXT_AND_IMAGE,
|
|
858
|
+
reasoningEfforts: {
|
|
859
|
+
off: "none",
|
|
860
|
+
...FULL_LEVELS
|
|
861
|
+
}
|
|
862
|
+
},
|
|
863
|
+
{
|
|
864
|
+
id: "gpt-6-luna",
|
|
865
|
+
name: "GPT-6-Luna",
|
|
866
|
+
contextWindow: CONTEXT_WINDOW,
|
|
867
|
+
maxTokens: MAX_TOKENS,
|
|
868
|
+
input: TEXT_AND_IMAGE,
|
|
869
|
+
reasoningEfforts: {
|
|
870
|
+
off: "none",
|
|
871
|
+
...FULL_LEVELS
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
id: "gpt-5.6-sol",
|
|
876
|
+
name: "GPT-5.6 Sol",
|
|
877
|
+
contextWindow: CONTEXT_WINDOW,
|
|
878
|
+
maxTokens: MAX_TOKENS,
|
|
879
|
+
input: TEXT_AND_IMAGE,
|
|
880
|
+
reasoningEfforts: {
|
|
881
|
+
off: null,
|
|
882
|
+
...FULL_LEVELS
|
|
883
|
+
}
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
id: "gpt-5.6-terra",
|
|
887
|
+
name: "GPT-5.6 Terra",
|
|
888
|
+
contextWindow: CONTEXT_WINDOW,
|
|
889
|
+
maxTokens: MAX_TOKENS,
|
|
890
|
+
input: TEXT_AND_IMAGE,
|
|
891
|
+
reasoningEfforts: {
|
|
892
|
+
off: null,
|
|
893
|
+
...FULL_LEVELS
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
{
|
|
897
|
+
id: "gpt-5.6-luna",
|
|
898
|
+
name: "GPT-5.6 Luna",
|
|
899
|
+
contextWindow: CONTEXT_WINDOW,
|
|
900
|
+
maxTokens: MAX_TOKENS,
|
|
901
|
+
input: TEXT_AND_IMAGE,
|
|
902
|
+
reasoningEfforts: {
|
|
903
|
+
off: null,
|
|
904
|
+
...FULL_LEVELS
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
id: "gpt-5.5",
|
|
909
|
+
name: "GPT-5.5",
|
|
910
|
+
contextWindow: CONTEXT_WINDOW,
|
|
911
|
+
maxTokens: MAX_TOKENS,
|
|
912
|
+
input: TEXT_AND_IMAGE,
|
|
913
|
+
reasoningEfforts: {
|
|
914
|
+
off: null,
|
|
915
|
+
minimal: "low",
|
|
916
|
+
low: "low",
|
|
917
|
+
medium: "medium",
|
|
918
|
+
high: "high",
|
|
919
|
+
xhigh: "xhigh"
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
];
|
|
923
|
+
/** 按 id 取能力;未知 id 返回 `undefined`(调用方决定是否省略字段而不是猜)。 */
|
|
924
|
+
function codexModelCapability(id) {
|
|
925
|
+
return CODEX_MODEL_CAPABILITIES.find((model) => model.id === id);
|
|
926
|
+
}
|
|
927
|
+
//#endregion
|
|
928
|
+
//#region src/host/model-capability-heal.ts
|
|
929
|
+
/**
|
|
930
|
+
* Repair a Codex model list that lost its capability fields.
|
|
931
|
+
*
|
|
932
|
+
* The official Models page writes a provider's whole `models` array into the
|
|
933
|
+
* profile's patch document. That array *replaces* the shipped baseline
|
|
934
|
+
* wholesale (DSH merges objects field by field but replaces arrays), so any
|
|
935
|
+
* row that only carries `id`/`name` hides the route's real context window,
|
|
936
|
+
* output cap and thinking levels from every selector — and the model picker
|
|
937
|
+
* shows grey `256K`/`32K` placeholders while the composer offers no reasoning
|
|
938
|
+
* levels. Rows like that were produced by the official candidate picker while
|
|
939
|
+
* its Codex candidates carried no capabilities; the bridge that feeds it is
|
|
940
|
+
* fixed, but the rows it already wrote are still in user profiles.
|
|
941
|
+
*
|
|
942
|
+
* This repair is deliberately **additive**:
|
|
943
|
+
*
|
|
944
|
+
* - it only fills a field that is *absent* — a value the user set, even a
|
|
945
|
+
* custom one, is never overwritten, so a deliberate `input: [text]` survives;
|
|
946
|
+
* - it never adds, removes, renames or reorders models, so a user's own
|
|
947
|
+
* selection, order and display names are preserved;
|
|
948
|
+
* - it only touches models this plugin's contract describes, so an id from a
|
|
949
|
+
* newer account catalog is left exactly as the user has it;
|
|
950
|
+
* - it returns `undefined` when nothing needs filling, which makes the caller
|
|
951
|
+
* a no-op and the whole operation idempotent — the write it triggers cannot
|
|
952
|
+
* echo back into another write.
|
|
953
|
+
*
|
|
954
|
+
* @module dsh-codex-auth/model-capability-heal
|
|
955
|
+
*/
|
|
956
|
+
/** The settings namespace owning the Codex route. */
|
|
957
|
+
const CODEX_SETTINGS_NAMESPACE = "llm-pi-ai";
|
|
958
|
+
/** Capability fields the repair may fill; all other fields are user-owned. */
|
|
959
|
+
const FILLABLE = [
|
|
960
|
+
"contextWindow",
|
|
961
|
+
"maxTokens",
|
|
962
|
+
"input",
|
|
963
|
+
"reasoningEfforts"
|
|
964
|
+
];
|
|
965
|
+
function record(value) {
|
|
966
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Plan the capability repair for one provider's `models` array.
|
|
970
|
+
*
|
|
971
|
+
* @param models - the row list as stored in the settings document.
|
|
972
|
+
* @returns the repaired rows plus the ids that were filled, or `undefined`
|
|
973
|
+
* when every described model already states its capabilities.
|
|
974
|
+
*/
|
|
975
|
+
function planCodexCapabilityRepair(models) {
|
|
976
|
+
if (!Array.isArray(models)) return void 0;
|
|
977
|
+
const filled = [];
|
|
978
|
+
let changed = false;
|
|
979
|
+
const repaired = models.map((row) => {
|
|
980
|
+
const model = record(row);
|
|
981
|
+
const id = model?.id;
|
|
982
|
+
if (model === void 0 || typeof id !== "string") return row;
|
|
983
|
+
const capability = codexModelCapability(id);
|
|
984
|
+
if (capability === void 0) return row;
|
|
985
|
+
if (FILLABLE.filter((field) => model[field] === void 0).length === 0) return row;
|
|
986
|
+
const add = {};
|
|
987
|
+
if (model.contextWindow === void 0) add.contextWindow = capability.contextWindow;
|
|
988
|
+
if (model.maxTokens === void 0) add.maxTokens = capability.maxTokens;
|
|
989
|
+
if (model.input === void 0) add.input = [...capability.input];
|
|
990
|
+
if (model.reasoningEfforts === void 0) add.reasoningEfforts = { ...capability.reasoningEfforts };
|
|
991
|
+
changed = true;
|
|
992
|
+
filled.push(id);
|
|
993
|
+
return {
|
|
994
|
+
...model,
|
|
995
|
+
...add
|
|
996
|
+
};
|
|
997
|
+
});
|
|
998
|
+
return changed ? {
|
|
999
|
+
models: repaired,
|
|
1000
|
+
filled
|
|
1001
|
+
} : void 0;
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Fill in missing Codex capabilities in the stored settings document.
|
|
1005
|
+
*
|
|
1006
|
+
* A missing namespace entry, a route that states its capabilities already, or
|
|
1007
|
+
* any error is reported as "nothing to do": this runs at startup beside the
|
|
1008
|
+
* credential mirror, so it must never fail activation or block boot. The
|
|
1009
|
+
* failure mode it guards against is cosmetic-plus-silent (degraded selectors),
|
|
1010
|
+
* never data loss.
|
|
1011
|
+
*
|
|
1012
|
+
* @param settings - the Host settings service.
|
|
1013
|
+
* @returns the ids that were filled, empty when no write was needed.
|
|
1014
|
+
*/
|
|
1015
|
+
async function healCodexModelCapabilities(settings) {
|
|
1016
|
+
const repair = planCodexCapabilityRepair(record(record(record(settings.describe().find((row) => row.ns === CODEX_SETTINGS_NAMESPACE)?.value)?.providers)?.[CODEX_PROVIDER])?.models);
|
|
1017
|
+
if (repair === void 0) return [];
|
|
1018
|
+
await settings.update(CODEX_SETTINGS_NAMESPACE, { providers: { [CODEX_PROVIDER]: { models: repair.models } } });
|
|
1019
|
+
return repair.filled;
|
|
1020
|
+
}
|
|
1021
|
+
//#endregion
|
|
823
1022
|
//#region src/host/adapter.ts
|
|
824
1023
|
/** OpenAI Codex adapter assembled from dsh's public pi-ai extension seam. */
|
|
825
1024
|
/** Keep the Codex stream open while the provider is still producing output. */
|
|
@@ -892,6 +1091,39 @@ const inject = [
|
|
|
892
1091
|
"agentDefaultModel",
|
|
893
1092
|
"llm"
|
|
894
1093
|
];
|
|
1094
|
+
/**
|
|
1095
|
+
* One-shot capability repair, run once the settings entries are addressable.
|
|
1096
|
+
*
|
|
1097
|
+
* Activation order between this plugin and `llm-pi-ai` is not guaranteed, so
|
|
1098
|
+
* the descriptor may not exist yet on the first attempt. The retry listens for
|
|
1099
|
+
* the settings service's own update notification and disconnects immediately
|
|
1100
|
+
* after the first attempt that can see the namespace: this has to stay a
|
|
1101
|
+
* one-shot, because rewriting a document while the user is editing it would
|
|
1102
|
+
* make their save lose its revision check.
|
|
1103
|
+
*/
|
|
1104
|
+
function scheduleCodexCapabilityHeal(ctx) {
|
|
1105
|
+
let settled = false;
|
|
1106
|
+
const attempt = () => {
|
|
1107
|
+
const settings = ctx.settings;
|
|
1108
|
+
if (!settings.describe().some((row) => row.ns === "llm-pi-ai")) return false;
|
|
1109
|
+
settled = true;
|
|
1110
|
+
healCodexModelCapabilities(settings).then((filled) => {
|
|
1111
|
+
if (filled.length > 0) ctx.logger.info(`dsh-codex-auth: filled missing Codex model capabilities for ${filled.join(", ")}`);
|
|
1112
|
+
}).catch((error) => {
|
|
1113
|
+
ctx.logger.warn("dsh-codex-auth: unable to fill Codex model capabilities", error);
|
|
1114
|
+
});
|
|
1115
|
+
return true;
|
|
1116
|
+
};
|
|
1117
|
+
ctx.effect(() => {
|
|
1118
|
+
if (attempt()) return () => {};
|
|
1119
|
+
const off = ctx.on("settings/document-updated", () => {
|
|
1120
|
+
if (!settled) attempt();
|
|
1121
|
+
});
|
|
1122
|
+
return () => {
|
|
1123
|
+
off();
|
|
1124
|
+
};
|
|
1125
|
+
}, "dsh-codex-auth: Codex model capability repair");
|
|
1126
|
+
}
|
|
895
1127
|
function apply(ctx) {
|
|
896
1128
|
const store = new CodexCredentialStore();
|
|
897
1129
|
const mirror = new CodexCredentialMirror(ctx.credentials, store);
|
|
@@ -910,6 +1142,7 @@ function apply(ctx) {
|
|
|
910
1142
|
registerCodexAuthRoutes(ctx, store, mirror);
|
|
911
1143
|
registerCodexModelRefreshRoute(ctx, store, { update: (ns, patch) => ctx.settings.update(ns, patch) });
|
|
912
1144
|
registerCodexGlobalModelRoute(ctx, ctx.agentDefaultModel, ctx.llm);
|
|
1145
|
+
scheduleCodexCapabilityHeal(ctx);
|
|
913
1146
|
}
|
|
914
1147
|
//#endregion
|
|
915
1148
|
export { CODEX_API_KEY_ENV, CODEX_API_KEY_REF, CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_STATUS_PATH, CODEX_GLOBAL_MODEL_PATH, CODEX_STREAM_IDLE_TIMEOUT_MS, CodexCredentialMirror, CodexCredentialStore, CodexUsageService, CodexWebAuth, apply, codexAuthStatus, createCodexAdapter, inject, loginCodex, logoutCodex, name, normalizeCodexUsagePayload, registerCodexAuthRoutes, trustedRequest };
|