dsh-codex-subscription 2.2.1 → 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/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";
@@ -662,6 +662,21 @@ function createSubscriptionConnection({ resolveMode = () => "sse", resolveProxy
662
662
  const namespace = randomUUID();
663
663
  const sessions = /* @__PURE__ */ new Set();
664
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
+ },
665
680
  async prepare(options = {}) {
666
681
  if (resolveMode() !== "websocket") return { options: {
667
682
  ...options,
@@ -716,6 +731,11 @@ const MAX_BYTES = 2 * 1024 * 1024;
716
731
  const MAX_AGE_MS = 10080 * 60 * 1e3;
717
732
  function createCompactionBridge({ enabled = () => false, threshold = () => 1e5, accountScope, diagnostic = () => {}, now = Date.now }) {
718
733
  const scope = new AsyncLocalStorage();
734
+ const stats = {
735
+ requests: 0,
736
+ checkpointsSaved: 0,
737
+ checkpointsReused: 0
738
+ };
719
739
  const wrapStream = (factory, options) => (async function* () {
720
740
  if (!enabled() || options.provider !== "openai-codex") {
721
741
  yield* factory(options);
@@ -775,6 +795,7 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
775
795
  }
776
796
  }
777
797
  };
798
+ stats.checkpointsSaved = Math.min(Number.MAX_SAFE_INTEGER, stats.checkpointsSaved + 1);
778
799
  }
779
800
  yield event;
780
801
  }
@@ -783,6 +804,7 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
783
804
  }
784
805
  })();
785
806
  return {
807
+ snapshot: () => ({ ...stats }),
786
808
  wrapAdapter(adapter) {
787
809
  return new Proxy(adapter, { get(target, property) {
788
810
  if (property === "stream") return (options) => wrapStream((o) => target.stream(o), options);
@@ -800,6 +822,11 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
800
822
  preparePayload(payload, contextWindow) {
801
823
  const state = scope.getStore();
802
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
+ }
803
830
  const configured = threshold();
804
831
  if (!Number.isSafeInteger(configured) || configured < 1e3) throw new Error("Invalid compaction threshold");
805
832
  const limit = Number.isFinite(contextWindow) && contextWindow >= 2e3 ? Math.min(configured, Math.floor(contextWindow / 2)) : configured;
@@ -902,6 +929,8 @@ const RPC_ENDPOINTS = Object.freeze([
902
929
  "runtime/install",
903
930
  "runtime/remove",
904
931
  "runtime/cancel",
932
+ "storage/status",
933
+ "storage/clear-forecast",
905
934
  "reset-credit/inspect",
906
935
  "reset-credit/prepare",
907
936
  "reset-credit/consume",
@@ -2832,7 +2861,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
2832
2861
  Object.freeze(["0.82.1", "0.85.1"]);
2833
2862
  //#endregion
2834
2863
  //#region src/version.js
2835
- const PACKAGE_VERSION = "2.2.1";
2864
+ const PACKAGE_VERSION = "2.2.2";
2836
2865
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
2837
2866
  //#endregion
2838
2867
  //#region src/model-catalog.js
@@ -2996,6 +3025,7 @@ function createOfficialModelCatalog(options = {}) {
2996
3025
  etag = nonEmpty$2(response.headers.get("etag")) ?? etag;
2997
3026
  revision += 1;
2998
3027
  outcome = "ok";
3028
+ options.onUpdated?.();
2999
3029
  return true;
3000
3030
  })();
3001
3031
  let rejectAborted;
@@ -3048,6 +3078,7 @@ function createOfficialModelCatalog(options = {}) {
3048
3078
  etag = void 0;
3049
3079
  refreshStatus = "idle";
3050
3080
  revision += 1;
3081
+ options.onUpdated?.();
3051
3082
  }
3052
3083
  });
3053
3084
  }
@@ -3700,7 +3731,7 @@ function createCodexImageTool(options) {
3700
3731
  localPath: options.originalImages.originalPath(original.assetId),
3701
3732
  ...metadata.background === void 0 ? {} : { background: metadata.background },
3702
3733
  ...metadata.quality === void 0 ? {} : { quality: metadata.quality },
3703
- ...metadata.size === void 0 ? {} : { size: metadata.size }
3734
+ size: `${original.width}x${original.height}`
3704
3735
  };
3705
3736
  if (exec.parent !== void 0) exec.deferContext(createUserMessage({
3706
3737
  content: imageContent(result),
@@ -3926,7 +3957,7 @@ function safeRequests(network) {
3926
3957
  return result;
3927
3958
  }
3928
3959
  /** Build a support report that deliberately excludes OAuth and account metadata. */
3929
- async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog }) {
3960
+ async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction }) {
3930
3961
  let account = { status: "unknown" };
3931
3962
  const issues = [];
3932
3963
  try {
@@ -3964,6 +3995,19 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
3964
3995
  account,
3965
3996
  login,
3966
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
+ ]) } : {},
3967
4011
  ...catalog && ["fallback", "online"].includes(catalog.source) && [
3968
4012
  "idle",
3969
4013
  "refreshing",
@@ -3986,6 +4030,10 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
3986
4030
  issues
3987
4031
  };
3988
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
+ }
3989
4037
  //#endregion
3990
4038
  //#region src/usage.js
3991
4039
  const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
@@ -4683,6 +4731,7 @@ function createQuotaForecastReader({ reader, enabled, now = Date.now, scope = ()
4683
4731
  return current === generation ? forecast.usage : usage;
4684
4732
  },
4685
4733
  clear: () => clearHistory(),
4734
+ storage: () => stateStore.usage(),
4686
4735
  clearCache() {
4687
4736
  generation += 1;
4688
4737
  reader.clear();
@@ -4768,6 +4817,22 @@ var QuotaForecastStateStore = class {
4768
4817
  async clear() {
4769
4818
  await rm(this.filename, { force: true });
4770
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
+ }
4771
4836
  };
4772
4837
  //#endregion
4773
4838
  //#region src/reset-credits.js
@@ -5066,6 +5131,17 @@ const publicError = (code, message) => ({
5066
5131
  });
5067
5132
  function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, runtimeManagement, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged }) {
5068
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
+ }
5069
5145
  if ([
5070
5146
  "runtime/status",
5071
5147
  "runtime/install",
@@ -5329,6 +5405,7 @@ function apply(ctx, config = {}) {
5329
5405
  });
5330
5406
  ctx.effect(() => ctx.on?.("plugin-manager/install-state", (value) => runtimeManagement.progress(value)));
5331
5407
  const modelCatalog = createOfficialModelCatalog({
5408
+ onUpdated: () => ctx.llm.emitAdaptersUpdated?.(),
5332
5409
  getAuth: (options) => resolveAuth(options),
5333
5410
  readCredential: (options) => store.read(PROVIDER, options),
5334
5411
  baseModels: () => baseProvider.getModels(),
@@ -5635,7 +5712,9 @@ function apply(ctx, config = {}) {
5635
5712
  preferences,
5636
5713
  login: coordinator.supportState(),
5637
5714
  network,
5638
- modelCatalog
5715
+ modelCatalog,
5716
+ connection,
5717
+ compaction
5639
5718
  }),
5640
5719
  modelCatalog,
5641
5720
  closeConnections: () => connection.dispose(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-subscription",
3
- "version": "2.2.1",
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",