@karlorz/codexhost-cli-darwin-arm64 0.13.2-1 → 0.13.2-3

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 CHANGED
@@ -7,7 +7,7 @@ Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.
7
7
  ## Install
8
8
 
9
9
  ```bash
10
- npm install -g @karlorz/codexhost-cli@0.13.2-1
10
+ npm install -g @karlorz/codexhost-cli@0.13.2-3
11
11
  ```
12
12
 
13
13
  Do not install this package directly. npm selects it through the optional dependencies of `@karlorz/codexhost-cli`.
@@ -1 +1 @@
1
- {"schemaVersion":1,"version":"0.13.2-1","distribution":"npm","target":"macos-arm64"}
1
+ {"schemaVersion":1,"version":"0.13.2-3","distribution":"npm","target":"macos-arm64"}
@@ -17429,7 +17429,7 @@ import { spawn as spawn2 } from "node:child_process";
17429
17429
  import { createServer } from "node:http";
17430
17430
 
17431
17431
  // packages/console-server/src/announcement.ts
17432
- var ANNOUNCEMENT_URL = "https://raw.githubusercontent.com/BytePioneer-AI/codex-host/main/docs/NOTICE.md";
17432
+ var ANNOUNCEMENT_URL = "https://raw.githubusercontent.com/karlorz/codex-host/main/docs/NOTICE.md";
17433
17433
  var MAX_DOCUMENT_BYTES = 16 * 1024;
17434
17434
  var TIMEOUT_MS = 5e3;
17435
17435
  function parseAnnouncement(document) {
@@ -20662,6 +20662,14 @@ th[aria-sort] .console-usage-sort__mark {
20662
20662
  otherSection: "Other",
20663
20663
  appearanceDescription: "Conversation display and local resource management.",
20664
20664
  appearanceGroup: "Appearance",
20665
+ hideCodexUsageBannerTitle: "Hide Codex usage warning",
20666
+ hideCodexUsageBannerDescription: "On by default. Hide the out-of-usage card on every Composer.",
20667
+ hideCodexUsageBannerHelpLabel: "About hiding the Codex usage warning",
20668
+ hideCodexUsageBannerHelp: Object.freeze([
20669
+ "The native card appears when the signed-in ChatGPT Codex quota is exhausted.",
20670
+ "When this is on, that card is hidden on every Composer, including Codex threads. Reset usage and Upgrade stay available in Settings.",
20671
+ "Turning this off only shows the native card again. Send on this Composer, including Codex threads, is already separated from that quota card."
20672
+ ]),
20665
20673
  cpaLiveVoiceTitle: "Override live voice",
20666
20674
  cpaLiveVoiceDescription: "On by default. Host creates the realtime call instead of ChatGPT.",
20667
20675
  cpaLiveVoiceHelpLabel: "About overriding live voice",
@@ -20945,6 +20953,14 @@ th[aria-sort] .console-usage-sort__mark {
20945
20953
  otherSection: "\u5176\u4ED6",
20946
20954
  appearanceDescription: "\u4F1A\u8BDD\u663E\u793A\u4E0E\u672C\u5730\u8D44\u6E90\u7BA1\u7406\u3002",
20947
20955
  appearanceGroup: "\u5916\u89C2",
20956
+ hideCodexUsageBannerTitle: "\u9690\u85CF Codex \u989D\u5EA6\u8B66\u544A",
20957
+ hideCodexUsageBannerDescription: "\u9ED8\u8BA4\u5F00\u542F\u3002\u5728\u6240\u6709 Composer \u4E0A\u9690\u85CF\u989D\u5EA6\u8017\u5C3D\u5361\u7247\u3002",
20958
+ hideCodexUsageBannerHelpLabel: "\u9690\u85CF Codex \u989D\u5EA6\u8B66\u544A\u8BF4\u660E",
20959
+ hideCodexUsageBannerHelp: Object.freeze([
20960
+ "\u5DF2\u767B\u5F55\u7684 ChatGPT Codex \u989D\u5EA6\u8017\u5C3D\u65F6\uFF0CDesktop \u4F1A\u663E\u793A\u539F\u751F\u8B66\u544A\u5361\u7247\u3002",
20961
+ "\u5F00\u542F\u540E\uFF0C\u6240\u6709 Composer \u90FD\u9690\u85CF\u8BE5\u5361\u7247\uFF0C\u5305\u62EC Codex \u4F1A\u8BDD\u3002\u300C\u91CD\u7F6E\u7528\u91CF\u300D\u548C\u300C\u5347\u7EA7\u300D\u4ECD\u53EF\u5728\u8BBE\u7F6E\u4E2D\u4F7F\u7528\u3002",
20962
+ "\u5173\u95ED\u6B64\u5F00\u5173\u53EA\u4F1A\u91CD\u65B0\u663E\u793A\u539F\u751F\u5361\u7247\u3002\u5F53\u524D Composer \u7684\u53D1\u9001\uFF08\u5305\u62EC Codex \u4F1A\u8BDD\uFF09\u5DF2\u4E0E\u8BE5\u989D\u5EA6\u5361\u7247\u5206\u79BB\u3002"
20963
+ ]),
20948
20964
  cpaLiveVoiceTitle: "\u6539\u5199\u5B9E\u65F6\u8BED\u97F3",
20949
20965
  cpaLiveVoiceDescription: "\u9ED8\u8BA4\u5F00\u542F\u3002\u7531 Host \u521B\u5EFA realtime \u901A\u8BDD\uFF0C\u800C\u4E0D\u662F ChatGPT\u3002",
20950
20966
  cpaLiveVoiceHelpLabel: "\u6539\u5199\u5B9E\u65F6\u8BED\u97F3\u8BF4\u660E",
@@ -26746,6 +26762,27 @@ ${error51.stderrTail}`] : []
26746
26762
  };
26747
26763
  }
26748
26764
 
26765
+ // src/renderer-codex-usage-banner-preference.ts
26766
+ var CODEX_USAGE_BANNER_STORAGE_KEY = "codexhost.codex-usage-banner-hidden.v1";
26767
+ var CODEX_USAGE_BANNER_CHANGE_EVENT = "codexhost:codex-usage-banner-changed";
26768
+ function readCodexUsageBannerPreference(owner) {
26769
+ if (!owner) return true;
26770
+ try {
26771
+ return owner.localStorage.getItem(CODEX_USAGE_BANNER_STORAGE_KEY) !== "0";
26772
+ } catch {
26773
+ return true;
26774
+ }
26775
+ }
26776
+ function writeCodexUsageBannerPreference(owner, enabled) {
26777
+ try {
26778
+ owner.localStorage.setItem(CODEX_USAGE_BANNER_STORAGE_KEY, enabled ? "1" : "0");
26779
+ } catch {
26780
+ return false;
26781
+ }
26782
+ owner.dispatchEvent(new Event(CODEX_USAGE_BANNER_CHANGE_EVENT));
26783
+ return true;
26784
+ }
26785
+
26749
26786
  // src/renderer-transcript-dom.ts
26750
26787
  var REASONING_SOFT_WRAP_STORAGE_KEY = "codexhost.reasoning-soft-wrap.v1";
26751
26788
  var REASONING_SOFT_WRAP_CHANGE_EVENT = "codexhost:reasoning-soft-wrap-changed";
@@ -27012,6 +27049,31 @@ ${error51.stderrTail}`] : []
27012
27049
  description.className = "settings-page-description";
27013
27050
  description.textContent = messages.appearanceDescription;
27014
27051
  const { group, card } = createPreferenceGroup(document2, messages.appearanceGroup);
27052
+ const usageBannerId = preferenceId("hide-codex-usage-banner");
27053
+ const usageBannerRow = createPreferenceItem(document2, {
27054
+ title: messages.hideCodexUsageBannerTitle,
27055
+ description: messages.hideCodexUsageBannerDescription,
27056
+ controlId: usageBannerId,
27057
+ help: {
27058
+ label: messages.hideCodexUsageBannerHelpLabel,
27059
+ lines: messages.hideCodexUsageBannerHelp
27060
+ }
27061
+ });
27062
+ const usageBanner = createPreferenceSwitch(
27063
+ document2,
27064
+ usageBannerId,
27065
+ usageBannerRow.description.id
27066
+ );
27067
+ usageBanner.checked = readCodexUsageBannerPreference(ownerWindow);
27068
+ usageBanner.addEventListener("change", () => {
27069
+ writeCodexUsageBannerPreference(ownerWindow, usageBanner.checked);
27070
+ });
27071
+ const syncUsageBanner = () => {
27072
+ usageBanner.checked = readCodexUsageBannerPreference(ownerWindow);
27073
+ };
27074
+ ownerWindow.addEventListener(CODEX_USAGE_BANNER_CHANGE_EVENT, syncUsageBanner);
27075
+ usageBannerRow.item.append(usageBanner);
27076
+ card.append(usageBannerRow.item);
27015
27077
  const liveVoiceId = preferenceId("cpa-live-voice");
27016
27078
  const liveVoiceRow = createPreferenceItem(document2, {
27017
27079
  title: messages.cpaLiveVoiceTitle,
@@ -27061,6 +27123,7 @@ ${error51.stderrTail}`] : []
27061
27123
  getLoadedSessionsClient
27062
27124
  );
27063
27125
  return () => {
27126
+ ownerWindow.removeEventListener(CODEX_USAGE_BANNER_CHANGE_EVENT, syncUsageBanner);
27064
27127
  ownerWindow.removeEventListener(CPA_LIVE_VOICE_CHANGE_EVENT, syncLiveVoice);
27065
27128
  ownerWindow.removeEventListener(REASONING_SOFT_WRAP_CHANGE_EVENT, sync);
27066
27129
  disposeIdleRelease();
@@ -18807,6 +18807,14 @@ ${error51.stderrTail}`] : []
18807
18807
  otherSection: "Other",
18808
18808
  appearanceDescription: "Conversation display and local resource management.",
18809
18809
  appearanceGroup: "Appearance",
18810
+ hideCodexUsageBannerTitle: "Hide Codex usage warning",
18811
+ hideCodexUsageBannerDescription: "On by default. Hide the out-of-usage card on every Composer.",
18812
+ hideCodexUsageBannerHelpLabel: "About hiding the Codex usage warning",
18813
+ hideCodexUsageBannerHelp: Object.freeze([
18814
+ "The native card appears when the signed-in ChatGPT Codex quota is exhausted.",
18815
+ "When this is on, that card is hidden on every Composer, including Codex threads. Reset usage and Upgrade stay available in Settings.",
18816
+ "Turning this off only shows the native card again. Send on this Composer, including Codex threads, is already separated from that quota card."
18817
+ ]),
18810
18818
  cpaLiveVoiceTitle: "Override live voice",
18811
18819
  cpaLiveVoiceDescription: "On by default. Host creates the realtime call instead of ChatGPT.",
18812
18820
  cpaLiveVoiceHelpLabel: "About overriding live voice",
@@ -19090,6 +19098,14 @@ ${error51.stderrTail}`] : []
19090
19098
  otherSection: "\u5176\u4ED6",
19091
19099
  appearanceDescription: "\u4F1A\u8BDD\u663E\u793A\u4E0E\u672C\u5730\u8D44\u6E90\u7BA1\u7406\u3002",
19092
19100
  appearanceGroup: "\u5916\u89C2",
19101
+ hideCodexUsageBannerTitle: "\u9690\u85CF Codex \u989D\u5EA6\u8B66\u544A",
19102
+ hideCodexUsageBannerDescription: "\u9ED8\u8BA4\u5F00\u542F\u3002\u5728\u6240\u6709 Composer \u4E0A\u9690\u85CF\u989D\u5EA6\u8017\u5C3D\u5361\u7247\u3002",
19103
+ hideCodexUsageBannerHelpLabel: "\u9690\u85CF Codex \u989D\u5EA6\u8B66\u544A\u8BF4\u660E",
19104
+ hideCodexUsageBannerHelp: Object.freeze([
19105
+ "\u5DF2\u767B\u5F55\u7684 ChatGPT Codex \u989D\u5EA6\u8017\u5C3D\u65F6\uFF0CDesktop \u4F1A\u663E\u793A\u539F\u751F\u8B66\u544A\u5361\u7247\u3002",
19106
+ "\u5F00\u542F\u540E\uFF0C\u6240\u6709 Composer \u90FD\u9690\u85CF\u8BE5\u5361\u7247\uFF0C\u5305\u62EC Codex \u4F1A\u8BDD\u3002\u300C\u91CD\u7F6E\u7528\u91CF\u300D\u548C\u300C\u5347\u7EA7\u300D\u4ECD\u53EF\u5728\u8BBE\u7F6E\u4E2D\u4F7F\u7528\u3002",
19107
+ "\u5173\u95ED\u6B64\u5F00\u5173\u53EA\u4F1A\u91CD\u65B0\u663E\u793A\u539F\u751F\u5361\u7247\u3002\u5F53\u524D Composer \u7684\u53D1\u9001\uFF08\u5305\u62EC Codex \u4F1A\u8BDD\uFF09\u5DF2\u4E0E\u8BE5\u989D\u5EA6\u5361\u7247\u5206\u79BB\u3002"
19108
+ ]),
19093
19109
  cpaLiveVoiceTitle: "\u6539\u5199\u5B9E\u65F6\u8BED\u97F3",
19094
19110
  cpaLiveVoiceDescription: "\u9ED8\u8BA4\u5F00\u542F\u3002\u7531 Host \u521B\u5EFA realtime \u901A\u8BDD\uFF0C\u800C\u4E0D\u662F ChatGPT\u3002",
19095
19111
  cpaLiveVoiceHelpLabel: "\u6539\u5199\u5B9E\u65F6\u8BED\u97F3\u8BF4\u660E",
@@ -22548,6 +22564,27 @@ Codex Fast mode prioritizes requests and may increase usage.`
22548
22564
  };
22549
22565
  }
22550
22566
 
22567
+ // src/renderer-codex-usage-banner-preference.ts
22568
+ var CODEX_USAGE_BANNER_STORAGE_KEY = "codexhost.codex-usage-banner-hidden.v1";
22569
+ var CODEX_USAGE_BANNER_CHANGE_EVENT = "codexhost:codex-usage-banner-changed";
22570
+ function readCodexUsageBannerPreference(owner) {
22571
+ if (!owner) return true;
22572
+ try {
22573
+ return owner.localStorage.getItem(CODEX_USAGE_BANNER_STORAGE_KEY) !== "0";
22574
+ } catch {
22575
+ return true;
22576
+ }
22577
+ }
22578
+ function writeCodexUsageBannerPreference(owner, enabled) {
22579
+ try {
22580
+ owner.localStorage.setItem(CODEX_USAGE_BANNER_STORAGE_KEY, enabled ? "1" : "0");
22581
+ } catch {
22582
+ return false;
22583
+ }
22584
+ owner.dispatchEvent(new Event(CODEX_USAGE_BANNER_CHANGE_EVENT));
22585
+ return true;
22586
+ }
22587
+
22551
22588
  // src/renderer-composer-dom.ts
22552
22589
  var CODEX_COMPOSER_SELECTOR = "[data-codex-composer-root]";
22553
22590
  var EDITOR_SELECTOR = 'textarea, [contenteditable="true"], [role="textbox"]';
@@ -22720,6 +22757,17 @@ Codex Fast mode prioritizes requests and may increase usage.`
22720
22757
  ].filter(isNativeContextUsageControlCandidate);
22721
22758
  return candidates.length === 1 ? candidates[0] ?? null : null;
22722
22759
  }
22760
+ function isNativeUsageBannerCandidate(element) {
22761
+ if (element.localName !== "aside") return false;
22762
+ if (typeof element.querySelector !== "function") return false;
22763
+ return element.querySelector("h3") !== null && element.querySelector("button") !== null;
22764
+ }
22765
+ function nativeUsageBannerForComposer(composer) {
22766
+ const candidates = [...composer.querySelectorAll("aside")].filter(
22767
+ isNativeUsageBannerCandidate
22768
+ );
22769
+ return candidates.length === 1 ? candidates[0] ?? null : null;
22770
+ }
22723
22771
  function captureNativeControl(element) {
22724
22772
  return element ? {
22725
22773
  element,
@@ -22741,6 +22789,12 @@ Codex Fast mode prioritizes requests and may increase usage.`
22741
22789
  restoreNativeControl(control.nativeContextUsageControl);
22742
22790
  control.nativeContextUsageControl = captureNativeControl(candidate);
22743
22791
  }
22792
+ function refreshNativeUsageBanner(control) {
22793
+ const candidate = nativeUsageBannerForComposer(control.composer);
22794
+ if (candidate === control.nativeUsageBanner?.element) return;
22795
+ restoreNativeControl(control.nativeUsageBanner);
22796
+ control.nativeUsageBanner = captureNativeControl(candidate);
22797
+ }
22744
22798
  function refreshNativeModelControl(control) {
22745
22799
  const candidate = nativeModelControlForComposer(control.composer);
22746
22800
  if (!candidate) return;
@@ -22859,12 +22913,20 @@ Codex Fast mode prioritizes requests and may increase usage.`
22859
22913
  refreshNativeContextUsageControl(control);
22860
22914
  refreshNativeModelControl(control);
22861
22915
  refreshNativePermissionModeControl(control);
22916
+ refreshNativeUsageBanner(control);
22862
22917
  refreshTrailingClusterPlacement(control);
22863
22918
  refreshUsagePlacement(control);
22864
22919
  refreshCreditsPlacement(control);
22865
22920
  setNativeControlHidden(control.nativeModelControl, hideModel);
22866
22921
  setNativeControlHidden(control.nativeContextUsageControl, false);
22867
22922
  setNativeControlHidden(control.nativePermissionModeControl, hidePermissionMode);
22923
+ setNativeControlHidden(
22924
+ control.nativeUsageBanner,
22925
+ readCodexUsageBannerPreference(preferenceOwner(control))
22926
+ );
22927
+ }
22928
+ function preferenceOwner(control) {
22929
+ return control.composer.ownerDocument?.defaultView ?? (typeof document !== "undefined" ? document.defaultView : null);
22868
22930
  }
22869
22931
  function mountComposerAgentControl(composer, composerId, sendButton, enabledAgents, onSelect, onDownload, onOpenProviderPicker, onSelectModel, onSelectThinking, onSelectPermissionMode, onOpenCommandMenu) {
22870
22932
  if (composer instanceof HTMLElement) composer.style.minWidth = "0";
@@ -22872,6 +22934,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22872
22934
  const nativeContextUsageControl = captureNativeControl(
22873
22935
  nativeContextUsageControlForComposer(composer)
22874
22936
  );
22937
+ const nativeUsageBanner = captureNativeControl(nativeUsageBannerForComposer(composer));
22875
22938
  const semanticNativePermissionModeControl = semanticNativePermissionModeControlForComposer(composer);
22876
22939
  const nativePermissionModeControl = captureNativeControl(semanticNativePermissionModeControl);
22877
22940
  const nativePermissionModeControlVerified = semanticNativePermissionModeControl !== null && nativePermissionModeControlForComposer(composer) === semanticNativePermissionModeControl;
@@ -22932,6 +22995,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
22932
22995
  nativeModelControl,
22933
22996
  nativePermissionModeControl,
22934
22997
  nativeContextUsageControl,
22998
+ nativeUsageBanner,
22935
22999
  nativePermissionModeControlVerified,
22936
23000
  credits,
22937
23001
  usage: null,
@@ -23012,7 +23076,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23012
23076
  control.harnessCommands.root.hidden = state.agent === "codex";
23013
23077
  control.harnessCommands.root.style.display = state.agent === "codex" ? "none" : "inline-flex";
23014
23078
  renderRendererCreditsControl(control.credits, accountCredits, locale);
23015
- return state.agent !== "codex" && adapterState === "ready" && !submissionBlocked;
23079
+ return !submissionBlocked && (state.agent === "codex" || adapterState === "ready");
23016
23080
  }
23017
23081
  function disposeComposerAgentControl(control) {
23018
23082
  if (control.sendDisabledBeforeSwitch !== null) {
@@ -23023,6 +23087,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23023
23087
  }
23024
23088
  restoreNativeControl(control.nativeModelControl);
23025
23089
  restoreNativeControl(control.nativeContextUsageControl);
23090
+ restoreNativeControl(control.nativeUsageBanner);
23026
23091
  restoreNativeControl(control.nativePermissionModeControl);
23027
23092
  control.credits.dispose();
23028
23093
  control.usage?.dispose();
@@ -23170,15 +23235,16 @@ Codex Fast mode prioritizes requests and may increase usage.`
23170
23235
  (name) => name.startsWith("__reactFiber$")
23171
23236
  );
23172
23237
  if (key) {
23173
- const owners = committedReactAncestors(
23238
+ const candidates = committedReactAncestors(
23174
23239
  Object.getOwnPropertyDescriptor(element, key)?.value
23175
23240
  ).filter(
23176
23241
  ({ memoizedProps: props }) => isRecord3(props) && "onLocalSubmitStart" in props && typeof props.submitDisabled === "boolean"
23177
23242
  );
23178
- return owners.filter((owner) => {
23179
- const gates = gateSubscriptions(owner);
23243
+ const owner = candidates.find((fiber) => {
23244
+ const gates = subtreeGateSubscriptions(fiber);
23180
23245
  return !gates.invalid && gates.reserve.length > 0;
23181
23246
  });
23247
+ return owner ? [owner] : candidates;
23182
23248
  }
23183
23249
  element = element.parentElement;
23184
23250
  }
@@ -23258,7 +23324,7 @@ Codex Fast mode prioritizes requests and may increase usage.`
23258
23324
  }
23259
23325
  const reserve = read.has("reserve.hardBlocked") && !read.has("reserve.active");
23260
23326
  const account = read.has("auth.authMethod") && read.has("limit.allowed");
23261
- if (account && (read.has("reserve.hardBlocked") || read.has("reserve.active"))) return "mixed";
23327
+ if (account && read.has("reserve.hardBlocked")) return "mixed";
23262
23328
  if (reserve) return "reserve";
23263
23329
  if (account) return "account";
23264
23330
  return null;
@@ -23276,6 +23342,27 @@ Codex Fast mode prioritizes requests and may increase usage.`
23276
23342
  return false;
23277
23343
  }
23278
23344
  }
23345
+ function projectionFor(projections, instance) {
23346
+ return projections?.get(instance);
23347
+ }
23348
+ function sameLogicalGate(subscriptions) {
23349
+ const first = subscriptions[0];
23350
+ if (!first) return true;
23351
+ return subscriptions.every((item) => item.store === first.store && item.atom === first.atom);
23352
+ }
23353
+ var MAX_SUBTREE_FIBERS = 1024;
23354
+ function subtreeFibers(root) {
23355
+ const result = [];
23356
+ const stack = [root.child];
23357
+ while (stack.length > 0) {
23358
+ const fiber = stack.pop();
23359
+ if (!isRecord3(fiber)) continue;
23360
+ result.push(fiber);
23361
+ if (result.length >= MAX_SUBTREE_FIBERS) return [];
23362
+ stack.push(fiber.child, fiber.sibling);
23363
+ }
23364
+ return result;
23365
+ }
23279
23366
  function gateSubscriptions(owner, projections) {
23280
23367
  const found = {
23281
23368
  account: [],
@@ -23286,23 +23373,34 @@ Codex Fast mode prioritizes requests and may increase usage.`
23286
23373
  const subscription = subscriptionAt(hook);
23287
23374
  const kind = subscription && gateKind(subscription);
23288
23375
  if (subscription && kind) {
23289
- if (kind !== "mixed" && snapshotsMatch(subscription, projections?.get(kind))) {
23376
+ if (kind !== "mixed" && snapshotsMatch(subscription, projectionFor(projections, subscription.instance))) {
23290
23377
  found[kind].push(subscription);
23291
23378
  } else found.invalid = true;
23292
23379
  }
23293
23380
  }
23294
23381
  return found;
23295
23382
  }
23383
+ function subtreeGateSubscriptions(owner, projections) {
23384
+ const combined = {
23385
+ account: [],
23386
+ reserve: [],
23387
+ invalid: false
23388
+ };
23389
+ for (const fiber of [owner, ...subtreeFibers(owner)]) {
23390
+ const gates = gateSubscriptions(fiber, projections);
23391
+ if (gates.invalid) combined.invalid = true;
23392
+ combined.account.push(...gates.account);
23393
+ combined.reserve.push(...gates.reserve);
23394
+ }
23395
+ if (!sameLogicalGate(combined.account) || !sameLogicalGate(combined.reserve)) {
23396
+ combined.invalid = true;
23397
+ }
23398
+ return combined;
23399
+ }
23296
23400
  function discoverGates(owner, projections) {
23297
- const gates = gateSubscriptions(owner, projections);
23401
+ const gates = subtreeGateSubscriptions(owner, projections);
23298
23402
  if (gates.invalid) return null;
23299
- const found = /* @__PURE__ */ new Map();
23300
- for (const kind of ["account", "reserve"]) {
23301
- const subscriptions = gates[kind];
23302
- if (subscriptions.length > 1) return null;
23303
- if (subscriptions[0]) found.set(kind, subscriptions[0]);
23304
- }
23305
- return found;
23403
+ return { account: gates.account, reserve: gates.reserve };
23306
23404
  }
23307
23405
  function project2({ subscriber, store, atom, instance, subscribeEffect }) {
23308
23406
  for (const [target, key] of [
@@ -23347,8 +23445,11 @@ Codex Fast mode prioritizes requests and may increase usage.`
23347
23445
  function isBound(owner, projections) {
23348
23446
  const alternate = isRecord3(owner.alternate) ? owner.alternate : null;
23349
23447
  const bound = [...projections];
23350
- return [owner, alternate].some((fiber) => {
23351
- const hooks = hooksOf(fiber);
23448
+ const subtrees = [owner, alternate].flatMap(
23449
+ (fiber) => fiber ? [[fiber, ...subtreeFibers(fiber)]] : []
23450
+ );
23451
+ return subtrees.some((fibers) => {
23452
+ const hooks = fibers.flatMap((fiber) => hooksOf(fiber));
23352
23453
  return bound.every(
23353
23454
  ({ subscriber, instance }) => hooks.some(
23354
23455
  (hook) => Array.isArray(hook.memoizedState) && hook.memoizedState[0] === subscriber && hook.next?.queue === instance
@@ -23375,10 +23476,12 @@ Codex Fast mode prioritizes requests and may increase usage.`
23375
23476
  const bind = () => {
23376
23477
  const current = owner ?? findSubmitOwner(composer);
23377
23478
  const gates = current && discoverGates(current, projections);
23378
- if (!current || !gates?.has("reserve")) throw new Error("Codex usage gate is unavailable");
23479
+ if (!current || !gates || gates.reserve.length === 0) {
23480
+ throw new Error("Codex usage gate is unavailable");
23481
+ }
23379
23482
  owner = current;
23380
- for (const [kind, gate] of gates) {
23381
- if (!projections.has(kind)) projections.set(kind, project2(gate));
23483
+ for (const gate of [...gates.reserve, ...gates.account]) {
23484
+ if (!projections.has(gate.instance)) projections.set(gate.instance, project2(gate));
23382
23485
  }
23383
23486
  return "bypassed";
23384
23487
  };
@@ -23392,7 +23495,6 @@ Codex Fast mode prioritizes requests and may increase usage.`
23392
23495
  release();
23393
23496
  retryAt = 0;
23394
23497
  }
23395
- if (owner && projections.size === 2) return "bypassed";
23396
23498
  if (Date.now() < retryAt) return owner ? "bypassed" : "unsupported";
23397
23499
  retryAt = Date.now() + RETRY_DELAY_MS;
23398
23500
  try {
@@ -30947,6 +31049,31 @@ ${detail}` : ""}`
30947
31049
  description.className = "settings-page-description";
30948
31050
  description.textContent = messages.appearanceDescription;
30949
31051
  const { group, card } = createPreferenceGroup(document2, messages.appearanceGroup);
31052
+ const usageBannerId = preferenceId("hide-codex-usage-banner");
31053
+ const usageBannerRow = createPreferenceItem(document2, {
31054
+ title: messages.hideCodexUsageBannerTitle,
31055
+ description: messages.hideCodexUsageBannerDescription,
31056
+ controlId: usageBannerId,
31057
+ help: {
31058
+ label: messages.hideCodexUsageBannerHelpLabel,
31059
+ lines: messages.hideCodexUsageBannerHelp
31060
+ }
31061
+ });
31062
+ const usageBanner = createPreferenceSwitch(
31063
+ document2,
31064
+ usageBannerId,
31065
+ usageBannerRow.description.id
31066
+ );
31067
+ usageBanner.checked = readCodexUsageBannerPreference(ownerWindow);
31068
+ usageBanner.addEventListener("change", () => {
31069
+ writeCodexUsageBannerPreference(ownerWindow, usageBanner.checked);
31070
+ });
31071
+ const syncUsageBanner = () => {
31072
+ usageBanner.checked = readCodexUsageBannerPreference(ownerWindow);
31073
+ };
31074
+ ownerWindow.addEventListener(CODEX_USAGE_BANNER_CHANGE_EVENT, syncUsageBanner);
31075
+ usageBannerRow.item.append(usageBanner);
31076
+ card.append(usageBannerRow.item);
30950
31077
  const liveVoiceId = preferenceId("cpa-live-voice");
30951
31078
  const liveVoiceRow = createPreferenceItem(document2, {
30952
31079
  title: messages.cpaLiveVoiceTitle,
@@ -30996,6 +31123,7 @@ ${detail}` : ""}`
30996
31123
  getLoadedSessionsClient
30997
31124
  );
30998
31125
  return () => {
31126
+ ownerWindow.removeEventListener(CODEX_USAGE_BANNER_CHANGE_EVENT, syncUsageBanner);
30999
31127
  ownerWindow.removeEventListener(CPA_LIVE_VOICE_CHANGE_EVENT, syncLiveVoice);
31000
31128
  ownerWindow.removeEventListener(REASONING_SOFT_WRAP_CHANGE_EVENT, sync);
31001
31129
  disposeIdleRelease();
@@ -39739,7 +39867,7 @@ ${usage_default}`;
39739
39867
  const currentCodexAccount = accounts?.accounts.find(
39740
39868
  ({ accountId }) => accountId === accounts.readyAccountId
39741
39869
  );
39742
- const externalSubmissionReady = renderComposerAgentControl(
39870
+ const usageGateAllowed = renderComposerAgentControl(
39743
39871
  mounted.control,
39744
39872
  controller.get(mounted.composer),
39745
39873
  composerAdapterState(mounted),
@@ -39753,7 +39881,7 @@ ${usage_default}`;
39753
39881
  currentCodexAccount ?? null,
39754
39882
  mounted.ownershipStatus === "error"
39755
39883
  );
39756
- showCodexUsageGateStatus(mounted, mounted.codexUsageGate.update(externalSubmissionReady));
39884
+ showCodexUsageGateStatus(mounted, mounted.codexUsageGate.update(usageGateAllowed));
39757
39885
  if (mounted.control.usage) {
39758
39886
  mounted.control.usage.onOpen = () => {
39759
39887
  void refreshThreadUsage(
@@ -41458,10 +41586,23 @@ ${usage_default}`;
41458
41586
  }
41459
41587
  }
41460
41588
  };
41589
+ const onUsageBannerPreference = () => {
41590
+ for (const mounted of mountedByComposer.values()) {
41591
+ const hideCodexControls = controller.isSwitching(mounted.composer) || controller.get(mounted.composer).agent !== "codex";
41592
+ reconcileComposerNativeControls(mounted.control, hideCodexControls, hideCodexControls);
41593
+ }
41594
+ };
41595
+ const onUsageBannerStorage = (event) => {
41596
+ if (event.key === CODEX_USAGE_BANNER_STORAGE_KEY || event.key === null) {
41597
+ onUsageBannerPreference();
41598
+ }
41599
+ };
41461
41600
  window.addEventListener("codexhost:draft-prewarm-policy-changed", onHostRouteChange);
41462
41601
  window.addEventListener("codexhost:draft-workspace", onDraftWorkspace);
41463
41602
  window.addEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
41464
41603
  window.addEventListener("focus", onWindowFocus);
41604
+ window.addEventListener(CODEX_USAGE_BANNER_CHANGE_EVENT, onUsageBannerPreference);
41605
+ window.addEventListener("storage", onUsageBannerStorage);
41465
41606
  delegationMention = installRendererDelegationMention(document, {
41466
41607
  onOpen: (editor) => {
41467
41608
  const composer = composerForElement(editor);
@@ -41625,6 +41766,8 @@ ${usage_default}`;
41625
41766
  window.removeEventListener("codexhost:draft-workspace", onDraftWorkspace);
41626
41767
  window.removeEventListener("codexhost:renderer-adapter-status", onAdapterStatus);
41627
41768
  window.removeEventListener("focus", onWindowFocus);
41769
+ window.removeEventListener(CODEX_USAGE_BANNER_CHANGE_EVENT, onUsageBannerPreference);
41770
+ window.removeEventListener("storage", onUsageBannerStorage);
41628
41771
  for (const state of harnessAvailabilityByHost.values()) {
41629
41772
  state.requestGeneration += 1;
41630
41773
  state.codexAccounts?.dispose();
package/bin/codexhost CHANGED
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karlorz/codexhost-cli-darwin-arm64",
3
- "version": "0.13.2-1",
3
+ "version": "0.13.2-3",
4
4
  "description": "Run Pi and Claude Code as first-class external harnesses inside Codex Desktop.",
5
5
  "type": "module",
6
6
  "files": [