dsh-codex-subscription 2.2.0 → 2.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.en.md +10 -6
- package/README.md +10 -6
- package/lib/client.js +1884 -1588
- package/lib/index.js +89 -9
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
3
|
-
import { closeOpenAICodexWebSocketSessions, resetOpenAICodexWebSocketDebugStats } from "@earendil-works/pi-ai/api/openai-codex-responses";
|
|
3
|
+
import { closeOpenAICodexWebSocketSessions, getOpenAICodexWebSocketDebugStats, resetOpenAICodexWebSocketDebugStats } from "@earendil-works/pi-ai/api/openai-codex-responses";
|
|
4
4
|
import { execFile, spawn } from "node:child_process";
|
|
5
5
|
import { request } from "node:https";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -553,14 +553,15 @@ async function withCodexNetwork(run, options = {}) {
|
|
|
553
553
|
}
|
|
554
554
|
if (options.websocket) activeWebSocketScopes += 1;
|
|
555
555
|
if (activeScopes === 0) {
|
|
556
|
-
|
|
556
|
+
const fetchBeforeScope = globalThis.fetch;
|
|
557
|
+
baseFetch = fetchBeforeScope;
|
|
557
558
|
scopedFetch = async (input, init) => {
|
|
558
559
|
const scope = networkScope.getStore();
|
|
559
|
-
if (scope === void 0) return
|
|
560
|
+
if (scope === void 0) return fetchBeforeScope(input, init);
|
|
560
561
|
const { options: scopedOptions, allowedHosts, resolved } = scope;
|
|
561
562
|
const proxyFetch = scopedOptions.fetchThroughProxy ?? fetchThroughProxy;
|
|
562
563
|
const target = new URL(typeof input === "string" || input instanceof URL ? input : input.url);
|
|
563
|
-
if (target.protocol !== "https:" || !allowedHosts.has(target.hostname)) return
|
|
564
|
+
if (target.protocol !== "https:" || !allowedHosts.has(target.hostname)) return fetchBeforeScope(input, init);
|
|
564
565
|
let proxy = resolved.get(target.hostname);
|
|
565
566
|
if (proxy === void 0) {
|
|
566
567
|
proxy = resolveCodexProxy({
|
|
@@ -571,7 +572,7 @@ async function withCodexNetwork(run, options = {}) {
|
|
|
571
572
|
}
|
|
572
573
|
const route = await proxy;
|
|
573
574
|
scopedOptions.onRoute?.(route.source);
|
|
574
|
-
const response = await (route.url === void 0 ?
|
|
575
|
+
const response = await (route.url === void 0 ? fetchBeforeScope(input, init) : proxyFetch(input, init, route.url));
|
|
575
576
|
return scopedOptions.transformResponse?.(response, target) ?? response;
|
|
576
577
|
};
|
|
577
578
|
globalThis.fetch = scopedFetch;
|
|
@@ -661,6 +662,21 @@ function createSubscriptionConnection({ resolveMode = () => "sse", resolveProxy
|
|
|
661
662
|
const namespace = randomUUID();
|
|
662
663
|
const sessions = /* @__PURE__ */ new Set();
|
|
663
664
|
return {
|
|
665
|
+
snapshot() {
|
|
666
|
+
const totals = Object.fromEntries([
|
|
667
|
+
"requests",
|
|
668
|
+
"connectionsCreated",
|
|
669
|
+
"connectionsReused",
|
|
670
|
+
"deltaRequests",
|
|
671
|
+
"websocketFailures",
|
|
672
|
+
"sseFallbacks"
|
|
673
|
+
].map((key) => [key, 0]));
|
|
674
|
+
for (const session of sessions) {
|
|
675
|
+
const stats = getOpenAICodexWebSocketDebugStats(session);
|
|
676
|
+
for (const key of Object.keys(totals)) if (Number.isSafeInteger(stats?.[key]) && stats[key] >= 0) totals[key] = Math.min(Number.MAX_SAFE_INTEGER, totals[key] + stats[key]);
|
|
677
|
+
}
|
|
678
|
+
return totals;
|
|
679
|
+
},
|
|
664
680
|
async prepare(options = {}) {
|
|
665
681
|
if (resolveMode() !== "websocket") return { options: {
|
|
666
682
|
...options,
|
|
@@ -715,6 +731,11 @@ const MAX_BYTES = 2 * 1024 * 1024;
|
|
|
715
731
|
const MAX_AGE_MS = 10080 * 60 * 1e3;
|
|
716
732
|
function createCompactionBridge({ enabled = () => false, threshold = () => 1e5, accountScope, diagnostic = () => {}, now = Date.now }) {
|
|
717
733
|
const scope = new AsyncLocalStorage();
|
|
734
|
+
const stats = {
|
|
735
|
+
requests: 0,
|
|
736
|
+
checkpointsSaved: 0,
|
|
737
|
+
checkpointsReused: 0
|
|
738
|
+
};
|
|
718
739
|
const wrapStream = (factory, options) => (async function* () {
|
|
719
740
|
if (!enabled() || options.provider !== "openai-codex") {
|
|
720
741
|
yield* factory(options);
|
|
@@ -774,6 +795,7 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
|
|
|
774
795
|
}
|
|
775
796
|
}
|
|
776
797
|
};
|
|
798
|
+
stats.checkpointsSaved = Math.min(Number.MAX_SAFE_INTEGER, stats.checkpointsSaved + 1);
|
|
777
799
|
}
|
|
778
800
|
yield event;
|
|
779
801
|
}
|
|
@@ -782,6 +804,7 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
|
|
|
782
804
|
}
|
|
783
805
|
})();
|
|
784
806
|
return {
|
|
807
|
+
snapshot: () => ({ ...stats }),
|
|
785
808
|
wrapAdapter(adapter) {
|
|
786
809
|
return new Proxy(adapter, { get(target, property) {
|
|
787
810
|
if (property === "stream") return (options) => wrapStream((o) => target.stream(o), options);
|
|
@@ -799,6 +822,11 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
|
|
|
799
822
|
preparePayload(payload, contextWindow) {
|
|
800
823
|
const state = scope.getStore();
|
|
801
824
|
if (!state) return payload;
|
|
825
|
+
if (!state.counted) {
|
|
826
|
+
state.counted = true;
|
|
827
|
+
stats.requests = Math.min(Number.MAX_SAFE_INTEGER, stats.requests + 1);
|
|
828
|
+
if (state.suffix) stats.checkpointsReused = Math.min(Number.MAX_SAFE_INTEGER, stats.checkpointsReused + 1);
|
|
829
|
+
}
|
|
802
830
|
const configured = threshold();
|
|
803
831
|
if (!Number.isSafeInteger(configured) || configured < 1e3) throw new Error("Invalid compaction threshold");
|
|
804
832
|
const limit = Number.isFinite(contextWindow) && contextWindow >= 2e3 ? Math.min(configured, Math.floor(contextWindow / 2)) : configured;
|
|
@@ -901,6 +929,8 @@ const RPC_ENDPOINTS = Object.freeze([
|
|
|
901
929
|
"runtime/install",
|
|
902
930
|
"runtime/remove",
|
|
903
931
|
"runtime/cancel",
|
|
932
|
+
"storage/status",
|
|
933
|
+
"storage/clear-forecast",
|
|
904
934
|
"reset-credit/inspect",
|
|
905
935
|
"reset-credit/prepare",
|
|
906
936
|
"reset-credit/consume",
|
|
@@ -2831,7 +2861,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
|
|
|
2831
2861
|
Object.freeze(["0.82.1", "0.85.1"]);
|
|
2832
2862
|
//#endregion
|
|
2833
2863
|
//#region src/version.js
|
|
2834
|
-
const PACKAGE_VERSION = "2.2.
|
|
2864
|
+
const PACKAGE_VERSION = "2.2.2";
|
|
2835
2865
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
2836
2866
|
//#endregion
|
|
2837
2867
|
//#region src/model-catalog.js
|
|
@@ -2995,6 +3025,7 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
2995
3025
|
etag = nonEmpty$2(response.headers.get("etag")) ?? etag;
|
|
2996
3026
|
revision += 1;
|
|
2997
3027
|
outcome = "ok";
|
|
3028
|
+
options.onUpdated?.();
|
|
2998
3029
|
return true;
|
|
2999
3030
|
})();
|
|
3000
3031
|
let rejectAborted;
|
|
@@ -3047,6 +3078,7 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
3047
3078
|
etag = void 0;
|
|
3048
3079
|
refreshStatus = "idle";
|
|
3049
3080
|
revision += 1;
|
|
3081
|
+
options.onUpdated?.();
|
|
3050
3082
|
}
|
|
3051
3083
|
});
|
|
3052
3084
|
}
|
|
@@ -3699,7 +3731,7 @@ function createCodexImageTool(options) {
|
|
|
3699
3731
|
localPath: options.originalImages.originalPath(original.assetId),
|
|
3700
3732
|
...metadata.background === void 0 ? {} : { background: metadata.background },
|
|
3701
3733
|
...metadata.quality === void 0 ? {} : { quality: metadata.quality },
|
|
3702
|
-
|
|
3734
|
+
size: `${original.width}x${original.height}`
|
|
3703
3735
|
};
|
|
3704
3736
|
if (exec.parent !== void 0) exec.deferContext(createUserMessage({
|
|
3705
3737
|
content: imageContent(result),
|
|
@@ -3925,7 +3957,7 @@ function safeRequests(network) {
|
|
|
3925
3957
|
return result;
|
|
3926
3958
|
}
|
|
3927
3959
|
/** Build a support report that deliberately excludes OAuth and account metadata. */
|
|
3928
|
-
async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog }) {
|
|
3960
|
+
async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction }) {
|
|
3929
3961
|
let account = { status: "unknown" };
|
|
3930
3962
|
const issues = [];
|
|
3931
3963
|
try {
|
|
@@ -3963,6 +3995,19 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3963
3995
|
account,
|
|
3964
3996
|
login,
|
|
3965
3997
|
requests: safeRequests(network),
|
|
3998
|
+
...connection ? { websocket: safeCounters(connection, [
|
|
3999
|
+
"requests",
|
|
4000
|
+
"connectionsCreated",
|
|
4001
|
+
"connectionsReused",
|
|
4002
|
+
"deltaRequests",
|
|
4003
|
+
"websocketFailures",
|
|
4004
|
+
"sseFallbacks"
|
|
4005
|
+
]) } : {},
|
|
4006
|
+
...compaction ? { compaction: safeCounters(compaction, [
|
|
4007
|
+
"requests",
|
|
4008
|
+
"checkpointsSaved",
|
|
4009
|
+
"checkpointsReused"
|
|
4010
|
+
]) } : {},
|
|
3966
4011
|
...catalog && ["fallback", "online"].includes(catalog.source) && [
|
|
3967
4012
|
"idle",
|
|
3968
4013
|
"refreshing",
|
|
@@ -3985,6 +4030,10 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3985
4030
|
issues
|
|
3986
4031
|
};
|
|
3987
4032
|
}
|
|
4033
|
+
function safeCounters(source, keys) {
|
|
4034
|
+
const values = source.snapshot();
|
|
4035
|
+
return Object.fromEntries(keys.filter((key) => Number.isSafeInteger(values?.[key]) && values[key] >= 0).map((key) => [key, values[key]]));
|
|
4036
|
+
}
|
|
3988
4037
|
//#endregion
|
|
3989
4038
|
//#region src/usage.js
|
|
3990
4039
|
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
@@ -4682,6 +4731,7 @@ function createQuotaForecastReader({ reader, enabled, now = Date.now, scope = ()
|
|
|
4682
4731
|
return current === generation ? forecast.usage : usage;
|
|
4683
4732
|
},
|
|
4684
4733
|
clear: () => clearHistory(),
|
|
4734
|
+
storage: () => stateStore.usage(),
|
|
4685
4735
|
clearCache() {
|
|
4686
4736
|
generation += 1;
|
|
4687
4737
|
reader.clear();
|
|
@@ -4767,6 +4817,22 @@ var QuotaForecastStateStore = class {
|
|
|
4767
4817
|
async clear() {
|
|
4768
4818
|
await rm(this.filename, { force: true });
|
|
4769
4819
|
}
|
|
4820
|
+
async usage() {
|
|
4821
|
+
try {
|
|
4822
|
+
const info = await lstat(this.filename);
|
|
4823
|
+
if (!info.isFile() || info.isSymbolicLink()) throw Error("Unexpected forecast cache entry");
|
|
4824
|
+
return {
|
|
4825
|
+
bytes: info.size,
|
|
4826
|
+
limit: this.maxBytes
|
|
4827
|
+
};
|
|
4828
|
+
} catch (error) {
|
|
4829
|
+
if (error.code === "ENOENT") return {
|
|
4830
|
+
bytes: 0,
|
|
4831
|
+
limit: this.maxBytes
|
|
4832
|
+
};
|
|
4833
|
+
throw error;
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4770
4836
|
};
|
|
4771
4837
|
//#endregion
|
|
4772
4838
|
//#region src/reset-credits.js
|
|
@@ -5065,6 +5131,17 @@ const publicError = (code, message) => ({
|
|
|
5065
5131
|
});
|
|
5066
5132
|
function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, runtimeManagement, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged }) {
|
|
5067
5133
|
return async (endpoint, payload, signal) => {
|
|
5134
|
+
if (endpoint === "storage/status" || endpoint === "storage/clear-forecast") try {
|
|
5135
|
+
signal.throwIfAborted();
|
|
5136
|
+
if (endpoint === "storage/clear-forecast") await usageReader.clear();
|
|
5137
|
+
return {
|
|
5138
|
+
ok: true,
|
|
5139
|
+
value: await usageReader.storage()
|
|
5140
|
+
};
|
|
5141
|
+
} catch (error) {
|
|
5142
|
+
if (signal.aborted) throw error;
|
|
5143
|
+
return publicError("operation-error", "Could not manage forecast cache");
|
|
5144
|
+
}
|
|
5068
5145
|
if ([
|
|
5069
5146
|
"runtime/status",
|
|
5070
5147
|
"runtime/install",
|
|
@@ -5328,6 +5405,7 @@ function apply(ctx, config = {}) {
|
|
|
5328
5405
|
});
|
|
5329
5406
|
ctx.effect(() => ctx.on?.("plugin-manager/install-state", (value) => runtimeManagement.progress(value)));
|
|
5330
5407
|
const modelCatalog = createOfficialModelCatalog({
|
|
5408
|
+
onUpdated: () => ctx.llm.emitAdaptersUpdated?.(),
|
|
5331
5409
|
getAuth: (options) => resolveAuth(options),
|
|
5332
5410
|
readCredential: (options) => store.read(PROVIDER, options),
|
|
5333
5411
|
baseModels: () => baseProvider.getModels(),
|
|
@@ -5634,7 +5712,9 @@ function apply(ctx, config = {}) {
|
|
|
5634
5712
|
preferences,
|
|
5635
5713
|
login: coordinator.supportState(),
|
|
5636
5714
|
network,
|
|
5637
|
-
modelCatalog
|
|
5715
|
+
modelCatalog,
|
|
5716
|
+
connection,
|
|
5717
|
+
compaction
|
|
5638
5718
|
}),
|
|
5639
5719
|
modelCatalog,
|
|
5640
5720
|
closeConnections: () => connection.dispose(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
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",
|