dsh-codex-connect 0.1.0-alpha.4.21 → 0.1.0-alpha.4.22
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/INSTALL.md +15 -2
- package/README.i18n.yaml +2 -2
- package/README.md +41 -6
- package/compatibility.json +3 -2
- package/docs/README.zh.md +41 -6
- package/docs/design.i18n.yaml +2 -2
- package/docs/design.md +1 -1
- package/docs/design.zh.md +1 -1
- package/lib/bin.js +2 -2
- package/lib/client.js +957 -248
- package/lib/index.d.ts +39 -11
- package/lib/index.js +2 -2
- package/lib/{src-Cxq99TvF.js → src-kwF-uZCG.js} +229 -57
- package/package.json +81 -54
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { basename, dirname, join, parse, resolve } from "node:path";
|
|
3
3
|
import z from "@deepseek-ai/schemastery";
|
|
4
|
-
import { deepEqualJson
|
|
4
|
+
import { deepEqualJson } from "@deepseek-ai/dsh-util-values";
|
|
5
5
|
import { createModels, defaultProviderAuthContext } from "@earendil-works/pi-ai";
|
|
6
6
|
import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
|
|
7
7
|
import { createUserMessage, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
@@ -165,13 +165,43 @@ var OpenAICodexCredentialStore = class {
|
|
|
165
165
|
}
|
|
166
166
|
};
|
|
167
167
|
//#endregion
|
|
168
|
+
//#region src/model-contract.ts
|
|
169
|
+
/** Node-free model catalog contract shared by the Host route and browser card. */
|
|
170
|
+
/** Same-origin endpoint exposing the complete Codex model catalog. */
|
|
171
|
+
const OPENAI_CODEX_MODEL_CATALOG_PATH = "/plugins/dsh-codex-connect/models";
|
|
172
|
+
const CONFIGURATION_LIMITS = Object.freeze({
|
|
173
|
+
"gpt-5.6-sol": 872e3,
|
|
174
|
+
"gpt-5.6-terra": 872e3,
|
|
175
|
+
"gpt-5.6-luna": 872e3,
|
|
176
|
+
"gpt-5.4": 1e6,
|
|
177
|
+
"gpt-5.5": 272e3,
|
|
178
|
+
"gpt-5.4-mini": 272e3
|
|
179
|
+
});
|
|
180
|
+
/** Keep unlisted or newer provider defaults usable without inventing a larger limit. */
|
|
181
|
+
function openAICodexContextLimit(id, contextWindow) {
|
|
182
|
+
const ceiling = Object.hasOwn(CONFIGURATION_LIMITS, id) ? CONFIGURATION_LIMITS[id] : void 0;
|
|
183
|
+
return ceiling === void 0 || ceiling < contextWindow ? {
|
|
184
|
+
maxContextWindow: contextWindow,
|
|
185
|
+
contextLimitSource: "catalog-default"
|
|
186
|
+
} : {
|
|
187
|
+
maxContextWindow: ceiling,
|
|
188
|
+
contextLimitSource: "codex-catalog"
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/** Whether a proposed local token budget fits the model's configuration range. */
|
|
192
|
+
function isValidOpenAICodexContextBudget(value, maximum) {
|
|
193
|
+
return Number.isSafeInteger(value) && value > 0 && value <= maximum;
|
|
194
|
+
}
|
|
195
|
+
//#endregion
|
|
168
196
|
//#region src/adapter.ts
|
|
169
197
|
/** OpenAI Codex adapter assembled from public dsh-llm-pi-ai extension points. */
|
|
170
198
|
/** Return a detached copy of the complete pi-ai Codex model catalog. */
|
|
171
199
|
function openAICodexModelCatalog() {
|
|
172
200
|
return openaiCodexProvider().getModels().map((model) => ({
|
|
173
201
|
id: model.id,
|
|
174
|
-
name: model.name
|
|
202
|
+
name: model.name,
|
|
203
|
+
contextWindow: model.contextWindow,
|
|
204
|
+
...openAICodexContextLimit(model.id, model.contextWindow)
|
|
175
205
|
}));
|
|
176
206
|
}
|
|
177
207
|
/** Provider idle ceiling used by the composite route. */
|
|
@@ -240,8 +270,9 @@ function requestProvider(provider, fastMode, proxyManager, resolveProxyUrl) {
|
|
|
240
270
|
}
|
|
241
271
|
};
|
|
242
272
|
}
|
|
243
|
-
/** Build the immutable profile consumed by the
|
|
244
|
-
function createOpenAICodexProfile(provider, fastMode, proxyManager, resolveProxyUrl) {
|
|
273
|
+
/** Build the immutable profile consumed by the DSH pi-ai adapter. */
|
|
274
|
+
function createOpenAICodexProfile(provider, fastMode, proxyManager, resolveProxyUrl, contextWindowOverrides) {
|
|
275
|
+
const effectiveProvider = contextWindowOverrides === void 0 ? provider : withOpenAICodexContextWindowOverrides(provider, contextWindowOverrides);
|
|
245
276
|
return {
|
|
246
277
|
provider: OPENAI_CODEX_PROVIDER,
|
|
247
278
|
displayName: "OpenAI Codex",
|
|
@@ -252,18 +283,59 @@ function createOpenAICodexProfile(provider, fastMode, proxyManager, resolveProxy
|
|
|
252
283
|
requestImageMaxBytes: OPENAI_CODEX_REQUEST_IMAGE_MAX_BYTES,
|
|
253
284
|
retryPolicy: resolveRetryPolicy(void 0, "dsh-codex-connect retryPolicy"),
|
|
254
285
|
configuredMaxTokens: /* @__PURE__ */ new Map(),
|
|
255
|
-
piProvider: requestProvider(
|
|
286
|
+
piProvider: requestProvider(effectiveProvider, fastMode, proxyManager, resolveProxyUrl)
|
|
256
287
|
};
|
|
257
288
|
}
|
|
258
289
|
/**
|
|
290
|
+
* Detach one provider and replace the advertised context window for the
|
|
291
|
+
* configured model ids. Request streaming itself is unaffected: pi-ai streams
|
|
292
|
+
* the caller-supplied model, so only the metadata Harness reads for context
|
|
293
|
+
* budgeting and compaction changes.
|
|
294
|
+
*/
|
|
295
|
+
function withOpenAICodexContextWindowOverrides(provider, overrides) {
|
|
296
|
+
const baselineModels = provider.getModels();
|
|
297
|
+
assertOpenAICodexContextWindowOverrides(overrides, baselineModels);
|
|
298
|
+
const replaced = baselineModels.map((model) => {
|
|
299
|
+
const contextWindow = overrides[model.id];
|
|
300
|
+
return contextWindow === void 0 ? model : {
|
|
301
|
+
...model,
|
|
302
|
+
contextWindow
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
return {
|
|
306
|
+
...provider,
|
|
307
|
+
getModels: () => replaced
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/** Reject unknown ids and out-of-range budgets before accepting settings or requests. */
|
|
311
|
+
function assertOpenAICodexContextWindowOverrides(overrides, catalog) {
|
|
312
|
+
const models = new Map(catalog.map((model) => [model.id, model]));
|
|
313
|
+
for (const [id, budget] of Object.entries(overrides ?? {})) {
|
|
314
|
+
const model = models.get(id);
|
|
315
|
+
if (model === void 0) throw new TypeError(`OpenAI Codex contextWindowOverrides contains unknown model id "${id}"`);
|
|
316
|
+
const { maxContextWindow } = openAICodexContextLimit(id, model.contextWindow);
|
|
317
|
+
if (budget !== null && !isValidOpenAICodexContextBudget(budget, maxContextWindow)) throw new TypeError(`OpenAI Codex contextWindowOverrides for "${id}" must be an integer from 1 to ${maxContextWindow} tokens; use null to restore the catalog default`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
259
321
|
* Create the Codex subscription adapter without requiring a dsh fork. The
|
|
260
322
|
* public pi-ai adapter owns Harness message conversion, image attachment
|
|
261
323
|
* resolution, streaming, reasoning metadata, and compaction behavior; this
|
|
262
324
|
* plugin supplies its provider-native OAuth token for each request.
|
|
263
325
|
*/
|
|
264
|
-
function createOpenAICodexAdapter(credentials, resolveAttachments, fastMode, visibleModelIds, proxyManager, resolveProxyUrl) {
|
|
326
|
+
function createOpenAICodexAdapter(credentials, resolveAttachments, fastMode, visibleModelIds, proxyManager, resolveProxyUrl, contextWindowOverrides) {
|
|
265
327
|
const provider = openaiCodexProvider();
|
|
266
|
-
|
|
328
|
+
let profiles;
|
|
329
|
+
let previousOverrides;
|
|
330
|
+
const currentProfiles = () => {
|
|
331
|
+
const overrides = contextWindowOverrides?.();
|
|
332
|
+
if (profiles === void 0 || !deepEqualJson(previousOverrides, overrides)) {
|
|
333
|
+
const profile = createOpenAICodexProfile(provider, fastMode, proxyManager, resolveProxyUrl, overrides);
|
|
334
|
+
previousOverrides = overrides === void 0 ? void 0 : { ...overrides };
|
|
335
|
+
profiles = /* @__PURE__ */ new Map([[OPENAI_CODEX_PROVIDER, profile]]);
|
|
336
|
+
}
|
|
337
|
+
return profiles;
|
|
338
|
+
};
|
|
267
339
|
const models = createModels({ credentials });
|
|
268
340
|
models.setProvider(provider);
|
|
269
341
|
class OpenAICodexAdapter extends PiAiAdapter {
|
|
@@ -276,7 +348,7 @@ function createOpenAICodexAdapter(credentials, resolveAttachments, fastMode, vis
|
|
|
276
348
|
}
|
|
277
349
|
}
|
|
278
350
|
return new OpenAICodexAdapter({
|
|
279
|
-
profiles:
|
|
351
|
+
profiles: currentProfiles,
|
|
280
352
|
resolveApiKey: async () => {
|
|
281
353
|
const operation = async () => (await models.getAuth(OPENAI_CODEX_PROVIDER))?.auth.apiKey;
|
|
282
354
|
return proxyManager?.run(resolveProxyUrl?.(), operation) ?? operation();
|
|
@@ -494,6 +566,8 @@ const OPENAI_CODEX_AUTH_STATUS_PATH = "/plugins/dsh-openai-codex/auth/status";
|
|
|
494
566
|
const OPENAI_CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-openai-codex/auth/login";
|
|
495
567
|
/** Plugin-owned logout endpoint consumed by its browser half. */
|
|
496
568
|
const OPENAI_CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-openai-codex/auth/logout";
|
|
569
|
+
/** Cancel only the pending authorization; never delete a stored credential. */
|
|
570
|
+
const OPENAI_CODEX_AUTH_CANCEL_PATH = "/plugins/dsh-openai-codex/auth/cancel";
|
|
497
571
|
//#endregion
|
|
498
572
|
//#region src/trusted-origins.ts
|
|
499
573
|
/** Owner-only allowlist for browser origins that may reach the Web OAuth routes. */
|
|
@@ -737,6 +811,8 @@ var FastModeRegistry = class {
|
|
|
737
811
|
/** Node-free Fast Mode route constants shared by Host and browser halves. */
|
|
738
812
|
/** GET/POST endpoint for one conversation's process-local Fast Mode state. */
|
|
739
813
|
const OPENAI_CODEX_FAST_MODE_PATH = "/plugins/dsh-openai-codex/fast-mode";
|
|
814
|
+
/** Default deadline for the complete interactive authorization, including the callback. */
|
|
815
|
+
const OPENAI_CODEX_AUTHORIZATION_TIMEOUT_MS = 6e5;
|
|
740
816
|
/** Stable, non-sensitive error returned when a browser origin needs CLI trust. */
|
|
741
817
|
const REMOTE_WEB_ORIGIN_NOT_TRUSTED = "remote-web-origin-not-trusted";
|
|
742
818
|
/** Redact provider diagnostics before they cross to the browser. */
|
|
@@ -744,9 +820,8 @@ function safeMessage(error) {
|
|
|
744
820
|
return (error instanceof Error ? error.message : String(error)).replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[redacted token]").replace(/(\b(?:code|token|refresh_token|access_token)=)[^&\s]+/giu, "$1[redacted]").slice(0, 1e3);
|
|
745
821
|
}
|
|
746
822
|
/** Reject with the prompt's abort reason while browser callback owns completion. */
|
|
747
|
-
function waitForPromptAbort(prompt) {
|
|
748
|
-
const signal = prompt.signal;
|
|
749
|
-
if (signal === void 0) return new Promise(() => {});
|
|
823
|
+
function waitForPromptAbort(prompt, operationSignal) {
|
|
824
|
+
const signal = prompt.signal === void 0 ? operationSignal : AbortSignal.any([prompt.signal, operationSignal]);
|
|
750
825
|
if (signal.aborted) return Promise.reject(signal.reason);
|
|
751
826
|
return new Promise((_resolve, reject) => {
|
|
752
827
|
signal.addEventListener("abort", () => {
|
|
@@ -763,15 +838,21 @@ var OpenAICodexWebAuth = class {
|
|
|
763
838
|
challenge;
|
|
764
839
|
challengeWaiters = [];
|
|
765
840
|
challengeTimer;
|
|
841
|
+
authorizationTimer;
|
|
842
|
+
transition;
|
|
843
|
+
disposed = false;
|
|
766
844
|
challengeTimeoutMs;
|
|
845
|
+
authorizationTimeoutMs;
|
|
767
846
|
proxyManager;
|
|
768
847
|
resolveProxyUrl;
|
|
769
848
|
constructor(store, options = {}) {
|
|
770
849
|
this.store = store;
|
|
771
850
|
this.challengeTimeoutMs = options.challengeTimeoutMs ?? 3e4;
|
|
851
|
+
this.authorizationTimeoutMs = options.authorizationTimeoutMs ?? 6e5;
|
|
772
852
|
this.proxyManager = options.proxyManager;
|
|
773
853
|
this.resolveProxyUrl = options.resolveProxyUrl ?? (() => void 0);
|
|
774
854
|
if (!Number.isFinite(this.challengeTimeoutMs) || this.challengeTimeoutMs <= 0) throw new TypeError("OpenAI Codex auth URL timeout must be a positive finite number");
|
|
855
|
+
if (!Number.isSafeInteger(this.authorizationTimeoutMs) || this.authorizationTimeoutMs <= 0 || this.authorizationTimeoutMs > 2147483647) throw new TypeError("OpenAI Codex authorization timeout must be a positive timer-safe integer");
|
|
775
856
|
}
|
|
776
857
|
/** Read current public state, consulting durable storage while idle. */
|
|
777
858
|
async status() {
|
|
@@ -781,6 +862,8 @@ var OpenAICodexWebAuth = class {
|
|
|
781
862
|
}
|
|
782
863
|
/** Start or join the current browser-login operation. */
|
|
783
864
|
async signIn() {
|
|
865
|
+
while (this.transition !== void 0) await this.transition;
|
|
866
|
+
if (this.disposed) throw new Error("OpenAI Codex plugin disposed");
|
|
784
867
|
if (this.operation === void 0) this.start();
|
|
785
868
|
if (this.challenge !== void 0) return this.challenge;
|
|
786
869
|
return new Promise((resolve, reject) => {
|
|
@@ -792,16 +875,35 @@ var OpenAICodexWebAuth = class {
|
|
|
792
875
|
}
|
|
793
876
|
/** Cancel any callback listener, wait for quiescence, then delete the credential. */
|
|
794
877
|
async signOut() {
|
|
795
|
-
this.
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
this.
|
|
878
|
+
await this.finishLogin("logout");
|
|
879
|
+
}
|
|
880
|
+
/** Cancel the pending authorization and retain any already stored account. */
|
|
881
|
+
async cancel() {
|
|
882
|
+
await this.finishLogin("cancel");
|
|
800
883
|
}
|
|
801
884
|
/** Stop the owned callback listener during plugin disposal. */
|
|
802
885
|
async dispose() {
|
|
803
|
-
this.
|
|
804
|
-
await this.
|
|
886
|
+
this.disposed = true;
|
|
887
|
+
await this.finishLogin("dispose");
|
|
888
|
+
}
|
|
889
|
+
async finishLogin(action) {
|
|
890
|
+
while (this.transition !== void 0) await this.transition;
|
|
891
|
+
const operation = this.operation;
|
|
892
|
+
this.cancelSignIn(/* @__PURE__ */ new Error(action === "dispose" ? "OpenAI Codex plugin disposed" : "OpenAI Codex sign-in cancelled"));
|
|
893
|
+
const transition = (async () => {
|
|
894
|
+
await operation?.catch(() => void 0);
|
|
895
|
+
this.challenge = void 0;
|
|
896
|
+
if (action === "logout") {
|
|
897
|
+
await logoutOpenAICodex(this.store);
|
|
898
|
+
this.state = { status: "signed-out" };
|
|
899
|
+
} else if (action === "cancel") this.state = await this.readStoredStatus();
|
|
900
|
+
})();
|
|
901
|
+
this.transition = transition;
|
|
902
|
+
try {
|
|
903
|
+
await transition;
|
|
904
|
+
} finally {
|
|
905
|
+
if (this.transition === transition) this.transition = void 0;
|
|
906
|
+
}
|
|
805
907
|
}
|
|
806
908
|
start() {
|
|
807
909
|
const cancellation = new AbortController();
|
|
@@ -812,9 +914,13 @@ var OpenAICodexWebAuth = class {
|
|
|
812
914
|
this.cancelSignIn(/* @__PURE__ */ new Error(`OpenAI Codex did not provide an authorization URL within ${String(this.challengeTimeoutMs)}ms`));
|
|
813
915
|
}, this.challengeTimeoutMs);
|
|
814
916
|
this.challengeTimer.unref();
|
|
917
|
+
this.authorizationTimer = setTimeout(() => {
|
|
918
|
+
this.cancelSignIn(/* @__PURE__ */ new Error("ChatGPT authorization expired. Please sign in again."));
|
|
919
|
+
}, this.authorizationTimeoutMs);
|
|
920
|
+
this.authorizationTimer.unref();
|
|
815
921
|
const login = () => loginOpenAICodex({
|
|
816
922
|
signal: cancellation.signal,
|
|
817
|
-
prompt: (prompt) => prompt.type === "select" ? Promise.resolve("browser") : waitForPromptAbort(prompt),
|
|
923
|
+
prompt: (prompt) => prompt.type === "select" ? Promise.resolve("browser") : waitForPromptAbort(prompt, cancellation.signal),
|
|
818
924
|
notify: (event) => {
|
|
819
925
|
this.onEvent(event);
|
|
820
926
|
}
|
|
@@ -838,6 +944,9 @@ var OpenAICodexWebAuth = class {
|
|
|
838
944
|
};
|
|
839
945
|
}).finally(() => {
|
|
840
946
|
this.clearChallengeTimer();
|
|
947
|
+
clearTimeout(this.authorizationTimer);
|
|
948
|
+
this.authorizationTimer = void 0;
|
|
949
|
+
this.challenge = void 0;
|
|
841
950
|
this.operation = void 0;
|
|
842
951
|
this.cancellation = void 0;
|
|
843
952
|
});
|
|
@@ -1045,10 +1154,11 @@ function fastModeBody(value) {
|
|
|
1045
1154
|
} : void 0;
|
|
1046
1155
|
}
|
|
1047
1156
|
/** Register the plugin-owned OAuth routes when the Web server is composed. */
|
|
1048
|
-
function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastModeOverride, proxyManager, resolveProxyUrl) {
|
|
1157
|
+
function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastModeOverride, proxyManager, resolveProxyUrl, authorizationTimeoutMs) {
|
|
1049
1158
|
const auth = new OpenAICodexWebAuth(store, {
|
|
1050
1159
|
proxyManager,
|
|
1051
|
-
resolveProxyUrl
|
|
1160
|
+
resolveProxyUrl,
|
|
1161
|
+
authorizationTimeoutMs
|
|
1052
1162
|
});
|
|
1053
1163
|
const storedFilename = store.filename;
|
|
1054
1164
|
const fastMode = fastModeOverride ?? new FastModeRegistry();
|
|
@@ -1083,6 +1193,20 @@ function registerOpenAICodexAuthRoutes(ctx, store, trustedOriginsOverride, fastM
|
|
|
1083
1193
|
}
|
|
1084
1194
|
}
|
|
1085
1195
|
}),
|
|
1196
|
+
ctx.webServer.register({
|
|
1197
|
+
kind: "exact",
|
|
1198
|
+
path: OPENAI_CODEX_AUTH_CANCEL_PATH,
|
|
1199
|
+
handler: async (req, res) => {
|
|
1200
|
+
if (req.method !== "POST") return json$4(res, 405, { error: "method not allowed" });
|
|
1201
|
+
if (!await authorize(req, res)) return;
|
|
1202
|
+
try {
|
|
1203
|
+
await auth.cancel();
|
|
1204
|
+
json$4(res, 200, await auth.status());
|
|
1205
|
+
} catch (error) {
|
|
1206
|
+
json$4(res, 500, { error: safeMessage(error) });
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
}),
|
|
1086
1210
|
ctx.webServer.register({
|
|
1087
1211
|
kind: "exact",
|
|
1088
1212
|
path: OPENAI_CODEX_AUTH_LOGOUT_PATH,
|
|
@@ -1157,6 +1281,30 @@ function normalizeOpenAICodexProxyUrl(value) {
|
|
|
1157
1281
|
function isValidOpenAICodexProxyUrl(value) {
|
|
1158
1282
|
return normalizeOpenAICodexProxyUrl(value) !== void 0;
|
|
1159
1283
|
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Whether a value is a bounded per-model context-window override map. Keys
|
|
1286
|
+
* are nonempty, unpadded model ids; values are positive safe integers or null
|
|
1287
|
+
* to restore that model's catalog default. The Host also checks catalog
|
|
1288
|
+
* membership and the model-specific configuration ceiling.
|
|
1289
|
+
*/
|
|
1290
|
+
function isValidOpenAICodexContextWindowOverrides(value) {
|
|
1291
|
+
if (!isRecord$1(value)) return false;
|
|
1292
|
+
const entries = Object.entries(value);
|
|
1293
|
+
if (entries.length > 256) return false;
|
|
1294
|
+
return entries.every(([modelId, window]) => modelId.length > 0 && modelId.trim() === modelId && (window === null || typeof window === "number" && Number.isSafeInteger(window) && window > 0));
|
|
1295
|
+
}
|
|
1296
|
+
/** Preserve per-model null masks until the Host has merged its settings layers. */
|
|
1297
|
+
function parseOpenAICodexContextWindowOverrides(value) {
|
|
1298
|
+
if (value === void 0 || value === null) return void 0;
|
|
1299
|
+
if (!isValidOpenAICodexContextWindowOverrides(value)) throw new TypeError("OpenAI Codex contextWindowOverrides must contain at most 256 nonempty model ids with positive safe-integer token budgets or null resets");
|
|
1300
|
+
return { ...value };
|
|
1301
|
+
}
|
|
1302
|
+
/** Resolve merged settings; whole-map or per-model null masks use catalog defaults. */
|
|
1303
|
+
function resolveOpenAICodexContextWindowOverrides(value) {
|
|
1304
|
+
const overrides = parseOpenAICodexContextWindowOverrides(value);
|
|
1305
|
+
if (overrides === void 0) return void 0;
|
|
1306
|
+
return Object.fromEntries(Object.entries(overrides).filter((entry) => entry[1] !== null));
|
|
1307
|
+
}
|
|
1160
1308
|
/** Default model used by the standalone search endpoint. */
|
|
1161
1309
|
const DEFAULT_OPENAI_CODEX_SEARCH_MODEL = "gpt-5.6-sol";
|
|
1162
1310
|
/** Default search mode, matching the official local Codex client. */
|
|
@@ -1169,6 +1317,7 @@ const DEFAULT_OPENAI_CODEX_SETTINGS = Object.freeze({
|
|
|
1169
1317
|
models: void 0,
|
|
1170
1318
|
enableProxy: false,
|
|
1171
1319
|
proxyUrl: DEFAULT_OPENAI_CODEX_PROXY_URL,
|
|
1320
|
+
contextWindowOverrides: void 0,
|
|
1172
1321
|
enableSearch: false,
|
|
1173
1322
|
enableImageTool: false,
|
|
1174
1323
|
enableImageGeneration: false,
|
|
@@ -1184,7 +1333,10 @@ function resolveOpenAICodexSettings(value) {
|
|
|
1184
1333
|
...value
|
|
1185
1334
|
};
|
|
1186
1335
|
if (!isValidOpenAICodexProxyUrl(resolved.proxyUrl)) throw new TypeError("OpenAI Codex proxyUrl must be an HTTP(S) origin without credentials or a path");
|
|
1187
|
-
return
|
|
1336
|
+
return {
|
|
1337
|
+
...resolved,
|
|
1338
|
+
contextWindowOverrides: resolveOpenAICodexContextWindowOverrides(resolved.contextWindowOverrides)
|
|
1339
|
+
};
|
|
1188
1340
|
}
|
|
1189
1341
|
/** Resolve the active proxy without treating a disabled value as a route. */
|
|
1190
1342
|
function resolveOpenAICodexProxyUrl(value) {
|
|
@@ -1200,6 +1352,7 @@ function decodeOpenAICodexSettings(value) {
|
|
|
1200
1352
|
const models = value["models"];
|
|
1201
1353
|
const enableProxy = value["enableProxy"];
|
|
1202
1354
|
const proxyUrl = value["proxyUrl"];
|
|
1355
|
+
const contextWindowOverrides = value["contextWindowOverrides"];
|
|
1203
1356
|
const enableSearch = value["enableSearch"];
|
|
1204
1357
|
const enableImageTool = value["enableImageTool"];
|
|
1205
1358
|
const enableImageGeneration = value["enableImageGeneration"];
|
|
@@ -1210,16 +1363,19 @@ function decodeOpenAICodexSettings(value) {
|
|
|
1210
1363
|
if (models !== void 0 && (!Array.isArray(models) || models.some((model) => typeof model !== "string"))) return void 0;
|
|
1211
1364
|
if (enableProxy !== void 0 && typeof enableProxy !== "boolean") return void 0;
|
|
1212
1365
|
if (proxyUrl !== void 0 && (typeof proxyUrl !== "string" || !isValidOpenAICodexProxyUrl(proxyUrl))) return void 0;
|
|
1366
|
+
if (contextWindowOverrides !== void 0 && contextWindowOverrides !== null && !isValidOpenAICodexContextWindowOverrides(contextWindowOverrides)) return void 0;
|
|
1213
1367
|
if (typeof enableSearch !== "boolean" || typeof enableImageTool !== "boolean") return void 0;
|
|
1214
1368
|
if (enableImageGeneration !== void 0 && typeof enableImageGeneration !== "boolean") return void 0;
|
|
1215
1369
|
if (typeof searchModel !== "string" || searchModel.trim().length === 0) return void 0;
|
|
1216
1370
|
if (searchMode !== "cached" && searchMode !== "indexed" && searchMode !== "live") return void 0;
|
|
1217
1371
|
if (searchContextSize !== "low" && searchContextSize !== "medium" && searchContextSize !== "high") return void 0;
|
|
1218
1372
|
if (typeof searchMaxOutputTokens !== "number" || !Number.isInteger(searchMaxOutputTokens) || searchMaxOutputTokens < 1) return void 0;
|
|
1373
|
+
const overrides = resolveOpenAICodexContextWindowOverrides(contextWindowOverrides);
|
|
1219
1374
|
return {
|
|
1220
1375
|
models: models === void 0 ? void 0 : [...new Set(models)],
|
|
1221
1376
|
enableProxy: enableProxy ?? false,
|
|
1222
1377
|
proxyUrl: proxyUrl === void 0 ? DEFAULT_OPENAI_CODEX_PROXY_URL : normalizeOpenAICodexProxyUrl(proxyUrl),
|
|
1378
|
+
contextWindowOverrides: overrides === void 0 ? void 0 : Object.freeze(overrides),
|
|
1223
1379
|
enableSearch,
|
|
1224
1380
|
enableImageTool,
|
|
1225
1381
|
enableImageGeneration: enableImageGeneration ?? false,
|
|
@@ -1514,8 +1670,8 @@ function registerOpenAICodexProxyRoutes(ctx, trustedOrigins, manager) {
|
|
|
1514
1670
|
//#region src/compatibility.ts
|
|
1515
1671
|
const COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
1516
1672
|
const SUPPORTED_NODE_RANGE = "^22.19.0 || >=24.0.0";
|
|
1517
|
-
const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.
|
|
1518
|
-
const
|
|
1673
|
+
const SUPPORTED_DSH_PLUGIN_API_VERSION = "0.1.2-alpha.2";
|
|
1674
|
+
const SUPPORTED_PI_AI_RANGE = "^0.84.2";
|
|
1519
1675
|
const PI_AI_PACKAGE = "@earendil-works/pi-ai";
|
|
1520
1676
|
const DSH_PLUGIN_API_PACKAGES = [
|
|
1521
1677
|
"@deepseek-ai/dsh-agent",
|
|
@@ -1530,6 +1686,7 @@ const DSH_PLUGIN_API_PACKAGES = [
|
|
|
1530
1686
|
"@deepseek-ai/dsh-session",
|
|
1531
1687
|
"@deepseek-ai/dsh-settings",
|
|
1532
1688
|
"@deepseek-ai/dsh-tools",
|
|
1689
|
+
"@deepseek-ai/dsh-util-values",
|
|
1533
1690
|
"@deepseek-ai/dsh-web"
|
|
1534
1691
|
];
|
|
1535
1692
|
const COMPATIBILITY_PACKAGES = [
|
|
@@ -1547,13 +1704,21 @@ const COMPATIBILITY_CONTRACT = {
|
|
|
1547
1704
|
},
|
|
1548
1705
|
piAi: {
|
|
1549
1706
|
package: PI_AI_PACKAGE,
|
|
1550
|
-
version:
|
|
1707
|
+
version: SUPPORTED_PI_AI_RANGE
|
|
1551
1708
|
}
|
|
1552
1709
|
};
|
|
1553
1710
|
const PACKAGE_JSON_SEARCH_DEPTH = 8;
|
|
1554
1711
|
function compareVersion(left, right) {
|
|
1555
1712
|
return left === right ? "compatible" : "incompatible";
|
|
1556
1713
|
}
|
|
1714
|
+
function piAiVersionStatus(value) {
|
|
1715
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/u.exec(value.trim());
|
|
1716
|
+
if (match === null) return "incompatible";
|
|
1717
|
+
const major = Number(match[1]);
|
|
1718
|
+
const minor = Number(match[2]);
|
|
1719
|
+
const patch = Number(match[3]);
|
|
1720
|
+
return major === 0 && minor === 84 && patch >= 2 ? "compatible" : "incompatible";
|
|
1721
|
+
}
|
|
1557
1722
|
function parseNodeVersion(value) {
|
|
1558
1723
|
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9A-Za-z.-]+)?$/u.exec(value.trim());
|
|
1559
1724
|
if (match === null) return void 0;
|
|
@@ -1579,11 +1744,11 @@ function nodeStatus(value) {
|
|
|
1579
1744
|
if (major === 22) return minor > 19 || minor === 19 && patch >= 0 ? "compatible" : "incompatible";
|
|
1580
1745
|
return major >= 24 ? "compatible" : "incompatible";
|
|
1581
1746
|
}
|
|
1582
|
-
function packageEntry(supported, installed) {
|
|
1747
|
+
function packageEntry(supported, installed, status = (value) => compareVersion(value, supported)) {
|
|
1583
1748
|
return {
|
|
1584
1749
|
supported,
|
|
1585
1750
|
installed: installed ?? null,
|
|
1586
|
-
status: installed === void 0 || installed === null || installed === "" ? "unknown" :
|
|
1751
|
+
status: installed === void 0 || installed === null || installed === "" ? "unknown" : status(installed)
|
|
1587
1752
|
};
|
|
1588
1753
|
}
|
|
1589
1754
|
function nodeEntry(installed) {
|
|
@@ -1605,7 +1770,7 @@ function evaluateCompatibility(input = {}) {
|
|
|
1605
1770
|
const packages = {
|
|
1606
1771
|
"@deepseek-ai/dsh-llm": packageEntry(SUPPORTED_DSH_PLUGIN_API_VERSION, suppliedPackages["@deepseek-ai/dsh-llm"]),
|
|
1607
1772
|
"@deepseek-ai/dsh-llm-pi-ai": packageEntry(SUPPORTED_DSH_PLUGIN_API_VERSION, suppliedPackages["@deepseek-ai/dsh-llm-pi-ai"]),
|
|
1608
|
-
[PI_AI_PACKAGE]: packageEntry(
|
|
1773
|
+
[PI_AI_PACKAGE]: packageEntry(SUPPORTED_PI_AI_RANGE, suppliedPackages[PI_AI_PACKAGE], piAiVersionStatus)
|
|
1609
1774
|
};
|
|
1610
1775
|
const node = nodeEntry(installedNode);
|
|
1611
1776
|
return {
|
|
@@ -1671,7 +1836,9 @@ const HIGHLIGHT_KINDS = [
|
|
|
1671
1836
|
"image-generation",
|
|
1672
1837
|
"oauth-history",
|
|
1673
1838
|
"model-visibility",
|
|
1674
|
-
"proxy-connection"
|
|
1839
|
+
"proxy-connection",
|
|
1840
|
+
"models-account",
|
|
1841
|
+
"context-budget"
|
|
1675
1842
|
];
|
|
1676
1843
|
function isHighlightKind(value) {
|
|
1677
1844
|
return typeof value === "string" && HIGHLIGHT_KINDS.includes(value);
|
|
@@ -2165,11 +2332,6 @@ function registerOpenAICodexUpdateRoutes(ctx, options, trustedOrigins) {
|
|
|
2165
2332
|
}, "dsh-codex-connect: update route");
|
|
2166
2333
|
}
|
|
2167
2334
|
//#endregion
|
|
2168
|
-
//#region src/model-contract.ts
|
|
2169
|
-
/** Node-free model catalog contract shared by the Host route and browser card. */
|
|
2170
|
-
/** Same-origin endpoint exposing the complete Codex model catalog. */
|
|
2171
|
-
const OPENAI_CODEX_MODEL_CATALOG_PATH = "/plugins/dsh-codex-connect/models";
|
|
2172
|
-
//#endregion
|
|
2173
2335
|
//#region src/model-routes.ts
|
|
2174
2336
|
function json$1(res, status, value) {
|
|
2175
2337
|
res.writeHead(status, {
|
|
@@ -2339,7 +2501,7 @@ function registerOpenAICodexOriginalImageRoute(ctx, trustedOrigins, assets) {
|
|
|
2339
2501
|
}
|
|
2340
2502
|
//#endregion
|
|
2341
2503
|
//#region src/version.ts
|
|
2342
|
-
const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.
|
|
2504
|
+
const CODEX_CONNECT_VERSION = "0.1.0-alpha.4.22";
|
|
2343
2505
|
//#endregion
|
|
2344
2506
|
//#region src/doctor.ts
|
|
2345
2507
|
/** Secret-free diagnostics and duplicate-provider guidance. */
|
|
@@ -2379,7 +2541,7 @@ async function diagnoseOpenAICodex(options = {}) {
|
|
|
2379
2541
|
if (state === "unreadable-metadata") hints.push("Harness could not inspect the OAuth file metadata; check the parent directory and file ownership.");
|
|
2380
2542
|
if (providerConflict) hints.push(openAICodexConflictMessage());
|
|
2381
2543
|
if (!providerConflict) hints.push("If Harness reports a duplicate openai-codex adapter, remove the legacy bundle or manual provider row.");
|
|
2382
|
-
if (compatibility.status === "incompatible") hints.push("Compatibility mismatch: install the declared DSH plugin API versions
|
|
2544
|
+
if (compatibility.status === "incompatible") hints.push("Compatibility mismatch: install the declared DSH plugin API versions with @earendil-works/pi-ai ^0.84.2, then run doctor again; no files are changed automatically.");
|
|
2383
2545
|
else if (compatibility.status === "unknown") hints.push("Compatibility is unknown: verify the declared DSH plugin API and @earendil-works/pi-ai versions, then run doctor again.");
|
|
2384
2546
|
return {
|
|
2385
2547
|
package: "dsh-codex-connect",
|
|
@@ -4063,11 +4225,13 @@ const name = "llm-openai-codex";
|
|
|
4063
4225
|
/** The model registry required before the provider can register. */
|
|
4064
4226
|
const inject = ["llm"];
|
|
4065
4227
|
/** Branded Host settings namespace for Codex Connect capability configuration. */
|
|
4066
|
-
const OPENAI_CODEX_SETTINGS_NS =
|
|
4228
|
+
const OPENAI_CODEX_SETTINGS_NS = OPENAI_CODEX_SETTINGS_NAMESPACE;
|
|
4067
4229
|
const Config = z.object({
|
|
4230
|
+
oauthTimeoutMs: z.number().step(1).min(1e3).max(18e5).default(OPENAI_CODEX_AUTHORIZATION_TIMEOUT_MS),
|
|
4068
4231
|
models: z.union([z.const(void 0), z.array(z.string())]),
|
|
4069
4232
|
enableProxy: z.boolean().default(false),
|
|
4070
4233
|
proxyUrl: z.string().default(DEFAULT_OPENAI_CODEX_PROXY_URL),
|
|
4234
|
+
contextWindowOverrides: z.transform(z.union([z.const(void 0), z.dict(z.union([z.const(null), z.number()]))]), parseOpenAICodexContextWindowOverrides),
|
|
4071
4235
|
enableSearch: z.boolean().default(false),
|
|
4072
4236
|
enableImageTool: z.boolean().default(false),
|
|
4073
4237
|
enableImageGeneration: z.boolean().default(false),
|
|
@@ -4092,6 +4256,12 @@ const Config = z.object({
|
|
|
4092
4256
|
* @param config - capability gates and standalone-search tuning.
|
|
4093
4257
|
*/
|
|
4094
4258
|
function apply(ctx, config) {
|
|
4259
|
+
const catalog = openAICodexModelCatalog();
|
|
4260
|
+
const validateSettings = (value) => {
|
|
4261
|
+
resolveOpenAICodexSettings(value);
|
|
4262
|
+
assertOpenAICodexContextWindowOverrides(value.contextWindowOverrides ?? void 0, catalog);
|
|
4263
|
+
};
|
|
4264
|
+
validateSettings(config);
|
|
4095
4265
|
let current = () => config;
|
|
4096
4266
|
const proxyManager = new OpenAICodexProxyManager();
|
|
4097
4267
|
const resolveProviderProxyUrl = () => resolveOpenAICodexProxyUrl(resolveOpenAICodexSettings(current()));
|
|
@@ -4102,9 +4272,9 @@ function apply(ctx, config) {
|
|
|
4102
4272
|
const fastMode = new FastModeRegistry();
|
|
4103
4273
|
assertNoOpenAICodexProviderConflict(ctx.llm.listProviders().map((provider) => provider.id));
|
|
4104
4274
|
new OpenAICodexTransport(ctx, credentials, proxyManager, resolveProviderProxyUrl);
|
|
4105
|
-
ctx.llm.registerAdapter([OPENAI_CODEX_PROVIDER], createOpenAICodexAdapter(credentials, () => ctx.get("attachments"), fastMode, () => resolveOpenAICodexSettings(current()).models, proxyManager, resolveProviderProxyUrl));
|
|
4275
|
+
ctx.llm.registerAdapter([OPENAI_CODEX_PROVIDER], createOpenAICodexAdapter(credentials, () => ctx.get("attachments"), fastMode, () => resolveOpenAICodexSettings(current()).models, proxyManager, resolveProviderProxyUrl, () => resolveOpenAICodexSettings(current()).contextWindowOverrides));
|
|
4106
4276
|
ctx.inject(["webServer"], (webCtx) => {
|
|
4107
|
-
registerOpenAICodexAuthRoutes(webCtx, credentials, trustedOrigins, fastMode, proxyManager, resolveProviderProxyUrl);
|
|
4277
|
+
registerOpenAICodexAuthRoutes(webCtx, credentials, trustedOrigins, fastMode, proxyManager, resolveProviderProxyUrl, config.oauthTimeoutMs);
|
|
4108
4278
|
registerOpenAICodexProxyRoutes(webCtx, trustedOrigins, proxyManager);
|
|
4109
4279
|
registerOpenAICodexUpdateRoutes(webCtx, { currentVersion: CODEX_CONNECT_VERSION }, trustedOrigins);
|
|
4110
4280
|
registerOpenAICodexModelCatalogRoute(webCtx, openAICodexModelCatalog, trustedOrigins);
|
|
@@ -4224,25 +4394,27 @@ function apply(ctx, config) {
|
|
|
4224
4394
|
]);
|
|
4225
4395
|
await proxyManager.dispose();
|
|
4226
4396
|
}, "dsh-codex-connect: optional capability lifecycle");
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4397
|
+
ctx.inject(["settings"], (settingsCtx) => {
|
|
4398
|
+
settingsCtx.settings.installSection(ctx, OPENAI_CODEX_SETTINGS_NS, Config, config, {
|
|
4399
|
+
validate(value) {
|
|
4400
|
+
validateSettings(value);
|
|
4401
|
+
if (value.enableProxy === true && !isValidOpenAICodexProxyUrl(value.proxyUrl)) throw new TypeError("OpenAI Codex proxyUrl must be an HTTP(S) origin without credentials or a path");
|
|
4402
|
+
},
|
|
4403
|
+
setSource(source) {
|
|
4404
|
+
current = source;
|
|
4405
|
+
},
|
|
4406
|
+
onChange() {
|
|
4407
|
+
const proxyIsActive = resolveProviderProxyUrl() !== void 0;
|
|
4408
|
+
if (proxyWasActive && !proxyIsActive) proxyManager.deactivate().catch((error) => {
|
|
4409
|
+
ctx.logger.error("dsh-codex-connect: could not deactivate the provider proxy");
|
|
4410
|
+
ctx.logger.error(error);
|
|
4411
|
+
});
|
|
4412
|
+
proxyWasActive = proxyIsActive;
|
|
4413
|
+
scheduleCapabilities();
|
|
4414
|
+
}
|
|
4415
|
+
});
|
|
4244
4416
|
});
|
|
4245
4417
|
scheduleCapabilities();
|
|
4246
4418
|
}
|
|
4247
4419
|
//#endregion
|
|
4248
|
-
export { OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS as $, openAICodexConflictMessage as A,
|
|
4420
|
+
export { OPENAI_CODEX_PROXY_PROBE_TIMEOUT_MS as $, openAICodexConflictMessage as A, openAICodexModelCatalog as At, DSH_PLUGIN_API_PACKAGES as B, OpenAICodexTransport as C, normalizeTrustedOrigin as Ct, IMAGE_GENERATE_TOOL_NAME as D, loginOpenAICodex as Dt, VIEW_IMAGE_TOOL_NAME as E, readOpenAICodexRateLimits as Et, parseOpenAICodexUpdateResult as F, assessCompatibility as G, SUPPORTED_DSH_PLUGIN_API_VERSION as H, parseOpenAICodexVersion as I, readInstalledPackageVersion as J, detectCompatibility as K, COMPATIBILITY_CONTRACT as L, OPENAI_CODEX_UPDATE_PATH as M, OPENAI_CODEX_PROVIDER as Mt, checkForOpenAICodexUpdate as N, OpenAICodexCredentialStore as Nt, assertNoOpenAICodexProviderConflict as O, logoutOpenAICodex as Ot, compareOpenAICodexVersions as P, openAICodexAuthPath as Pt, OPENAI_CODEX_PROXY_CANDIDATE_LIMIT as Q, COMPATIBILITY_PACKAGES as R, OPENAI_CODEX_TRANSPORT_SERVICE as S, OpenAICodexTrustedOriginsStore as St, isOpenAICodexTransportError as T, parseOpenAICodexUsage as Tt, SUPPORTED_NODE_RANGE as U, PI_AI_PACKAGE as V, SUPPORTED_PI_AI_RANGE as W, OPENAI_CODEX_PROXY_TEST_PATH as X, OPENAI_CODEX_PROXY_DETECT_PATH as Y, OPENAI_CODEX_LOCAL_PROXY_CANDIDATES as Z, OPENAI_CODEX_IMAGE_MAX_RESPONSE_BYTES as _, OPENAI_CODEX_FAST_MODE_PATH as _t, name as a, DEFAULT_OPENAI_CODEX_SEARCH_CONTEXT_SIZE as at, OPENAI_CODEX_TRANSPORT_API_VERSION as b, OPENAI_CODEX_FAST_MODE_MAX_SESSION_ID_LENGTH as bt, migrateOpenAICodexSearchHistory as c, DEFAULT_OPENAI_CODEX_SEARCH_MODEL as ct, OPENAI_CODEX_SEARCH_URL as d, decodeOpenAICodexSettings as dt, OPENAI_CODEX_PROXY_PROBE_URL as et, OpenAICodexSearchProvider as f, isValidOpenAICodexContextWindowOverrides as ft, OPENAI_CODEX_IMAGE_MAX_ERROR_BYTES as g, resolveOpenAICodexSettings as gt, OPENAI_CODEX_IMAGE_MAX_COUNT as h, resolveOpenAICodexProxyUrl as ht, inject as i, DEFAULT_OPENAI_CODEX_PROXY_URL as it, CODEX_CONNECT_VERSION as j, OPENAI_CODEX_AUTH_FILENAME as jt, diagnoseOpenAICodex as k, openAICodexAuthStatus as kt, OPENAI_CODEX_BASE_URL as l, DEFAULT_OPENAI_CODEX_SETTINGS as lt, OPENAI_CODEX_IMAGE_GENERATION_URL as m, normalizeOpenAICodexProxyUrl as mt, OPENAI_CODEX_SETTINGS_NS as n, detectOpenAICodexProxies as nt, OPENAI_CODEX_HISTORY_BACKUP_SUFFIX as o, DEFAULT_OPENAI_CODEX_SEARCH_MAX_OUTPUT_TOKENS as ot, mapOpenAICodexSearchResponse as p, isValidOpenAICodexProxyUrl as pt, evaluateCompatibility as q, apply as r, listOpenAICodexProxyCandidates as rt, OPENAI_CODEX_SEARCH_MODEL_REQUEST_EVENT as s, DEFAULT_OPENAI_CODEX_SEARCH_MODE as st, Config as t, OpenAICodexProxyManager as tt, OPENAI_CODEX_SEARCH_PROVIDER as u, OPENAI_CODEX_SETTINGS_NAMESPACE as ut, OPENAI_CODEX_IMAGE_PROMPT_MAX_LENGTH as v, FastModeRegistry as vt, OpenAICodexTransportError as w, OPENAI_CODEX_USAGE_URL as wt, OPENAI_CODEX_TRANSPORT_ERROR_CODES as x, isFastModeSessionId as xt, OPENAI_CODEX_IMAGE_REQUEST_TIMEOUT_MS as y, OPENAI_CODEX_FAST_MODE_MAX_SESSIONS as yt, COMPATIBILITY_SCHEMA_VERSION as z };
|