@bitkyc08/opencodex 2.49.0 → 2.51.0

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.
Files changed (141) hide show
  1. package/AGENTS_INSTALL.md +9 -1
  2. package/README.md +3 -0
  3. package/bin/ocx.mjs +222 -71
  4. package/gui/dist/assets/index-D7BdZpZm.js +115 -0
  5. package/gui/dist/index.html +1 -1
  6. package/package.json +1 -1
  7. package/src/adapters/qoder/adapter.ts +69 -1
  8. package/src/adapters/qoder/scaffold-guard.ts +233 -0
  9. package/src/claude/agents-inject.ts +29 -5
  10. package/src/claude/desktop-3p.ts +31 -3
  11. package/src/claude/gateway-cache.ts +12 -21
  12. package/src/claude/inbound.ts +17 -5
  13. package/src/cli/account-api.ts +18 -3
  14. package/src/cli/account-auth.ts +8 -1
  15. package/src/cli/account-extended.ts +2 -1
  16. package/src/cli/account.ts +1 -0
  17. package/src/cli/capabilities.ts +43 -1
  18. package/src/cli/claude-agent-startup-sync.ts +26 -1
  19. package/src/cli/claude.ts +138 -20
  20. package/src/cli/config-command.ts +67 -1
  21. package/src/cli/connect.ts +181 -14
  22. package/src/cli/dispatch.ts +53 -9
  23. package/src/cli/doctor.ts +9 -2
  24. package/src/cli/ensure-desired-integrations.ts +10 -0
  25. package/src/cli/gui-pair-client.ts +1 -12
  26. package/src/cli/help.ts +4 -1
  27. package/src/cli/hub.ts +367 -0
  28. package/src/cli/index.ts +99 -31
  29. package/src/cli/launcher-context.ts +1 -1
  30. package/src/cli/models-runtime.ts +8 -3
  31. package/src/cli/observe.ts +13 -3
  32. package/src/cli/registry.ts +43 -3
  33. package/src/cli/status.ts +325 -5
  34. package/src/cli/version-skew.ts +4 -1
  35. package/src/cli.ts +2 -2
  36. package/src/client/catalog-compatibility.ts +192 -0
  37. package/src/client/connect.ts +31 -0
  38. package/src/client/hub-client.ts +52 -0
  39. package/src/client/hub-state.ts +214 -0
  40. package/src/clients/config-export/zcode.ts +24 -0
  41. package/src/codex/account-runtime-state.ts +6 -1
  42. package/src/codex/account-store.ts +72 -9
  43. package/src/codex/account-usability.ts +50 -13
  44. package/src/codex/auth-api.ts +156 -28
  45. package/src/codex/auth-context.ts +21 -0
  46. package/src/codex/catalog/effort.ts +67 -8
  47. package/src/codex/catalog/parsing.ts +23 -0
  48. package/src/codex/catalog/provider-fetch.ts +71 -2
  49. package/src/codex/catalog/sync.ts +99 -0
  50. package/src/codex/codex-write-lock.ts +11 -2
  51. package/src/codex/desired-state.ts +47 -1
  52. package/src/codex/inject-coordination.ts +10 -5
  53. package/src/codex/inject.ts +29 -12
  54. package/src/codex/loopback-target.ts +45 -0
  55. package/src/codex/quota-auto-refresh.ts +6 -1
  56. package/src/codex/quota.ts +54 -8
  57. package/src/codex/routing.ts +48 -1
  58. package/src/codex/runtime.ts +37 -3
  59. package/src/codex/sync.ts +29 -9
  60. package/src/codex/warmup.ts +21 -4
  61. package/src/combos/index.ts +2 -0
  62. package/src/combos/resolve.ts +52 -0
  63. package/src/config/pending-teardown.ts +1 -1
  64. package/src/config.ts +184 -12
  65. package/src/generated/compatibility-version.json +188 -116
  66. package/src/grok/status.ts +9 -1
  67. package/src/integrations/config-io.ts +54 -1
  68. package/src/lib/bun-runtime.ts +1 -1
  69. package/src/lib/errors.ts +8 -0
  70. package/src/lib/gui-pair-capability.ts +27 -0
  71. package/src/lib/local-destinations.ts +162 -0
  72. package/src/lib/package-tree-integrity.ts +1 -1
  73. package/src/lib/privacy.ts +25 -0
  74. package/src/lib/process-control.ts +130 -20
  75. package/src/lib/service-secrets.ts +28 -0
  76. package/src/lib/test-home-guard.ts +49 -0
  77. package/src/oauth/health.ts +47 -12
  78. package/src/oauth/index.ts +46 -8
  79. package/src/oauth/token-guardian.ts +32 -6
  80. package/src/providers/google-ai-studio-model-discovery.ts +74 -0
  81. package/src/providers/opencode-go-transport.ts +9 -1
  82. package/src/providers/opencode-zen-rate-limit.ts +75 -0
  83. package/src/providers/quota.ts +20 -1
  84. package/src/providers/registry.ts +35 -6
  85. package/src/remote/hub-state.ts +182 -0
  86. package/src/server/auth-cors.ts +11 -0
  87. package/src/server/chat-completions.ts +10 -7
  88. package/src/server/chat-native.ts +10 -1
  89. package/src/server/claude-messages.ts +12 -6
  90. package/src/server/hub-state.ts +98 -0
  91. package/src/server/images.ts +2 -2
  92. package/src/server/index.ts +149 -8
  93. package/src/server/management/api-access.ts +14 -3
  94. package/src/server/management/config-routes.ts +2 -2
  95. package/src/server/management/cursor-integration-routes.ts +13 -4
  96. package/src/server/management/logs-usage-routes.ts +4 -1
  97. package/src/server/management/model-rows.ts +16 -1
  98. package/src/server/management/oauth-account-routes.ts +6 -2
  99. package/src/server/management/provider-routes.ts +9 -2
  100. package/src/server/management/request-history-routes.ts +4 -2
  101. package/src/server/management/route-registry.ts +5 -4
  102. package/src/server/management/shared.ts +66 -3
  103. package/src/server/management-api.ts +1 -1
  104. package/src/server/proxy-liveness.ts +7 -1
  105. package/src/server/request-decompress.ts +91 -3
  106. package/src/server/request-log-conversation.ts +41 -1
  107. package/src/server/request-log.ts +10 -0
  108. package/src/server/responses/codex-auth-error.ts +18 -1
  109. package/src/server/responses/codex-ws-exchange.ts +36 -4
  110. package/src/server/responses/codex-ws-wire.ts +76 -5
  111. package/src/server/responses/compact.ts +28 -11
  112. package/src/server/responses/context-overflow.ts +11 -0
  113. package/src/server/responses/core.ts +201 -48
  114. package/src/server/responses/policy-fallback.ts +13 -3
  115. package/src/server/search.ts +2 -2
  116. package/src/server/system-env-shell.ts +14 -2
  117. package/src/server/system-env.ts +106 -14
  118. package/src/service.ts +965 -68
  119. package/src/types/accounts.ts +18 -0
  120. package/src/types/config.ts +93 -4
  121. package/src/types/provider.ts +56 -0
  122. package/src/types.ts +4 -0
  123. package/src/update/badge.ts +3 -2
  124. package/src/update/index.ts +317 -64
  125. package/src/update/install-detection.d.mts +6 -0
  126. package/src/update/install-detection.mjs +73 -0
  127. package/src/update/job.ts +101 -49
  128. package/src/update/pnpm-global-install.d.mts +144 -0
  129. package/src/update/pnpm-global-install.mjs +591 -0
  130. package/src/update/pnpm-invocation.d.mts +43 -0
  131. package/src/update/pnpm-invocation.mjs +141 -0
  132. package/src/update/registry-integrity.d.mts +16 -0
  133. package/src/update/registry-integrity.mjs +37 -0
  134. package/src/update/transactional-install.d.mts +1 -1
  135. package/src/update/transactional-install.mjs +101 -7
  136. package/src/update/tray-update-plan.mjs +1 -1
  137. package/src/vision/plan.ts +13 -3
  138. package/src/vision/routed-describe.ts +51 -20
  139. package/src/web-search/ollama-executor.ts +127 -0
  140. package/src/web-search/passthrough-bridge.ts +761 -0
  141. package/gui/dist/assets/index-BtyONQrZ.js +0 -115
@@ -34,4 +34,22 @@ export interface CodexAccountCredentialRecord {
34
34
  lastCodexValidatedAt?: number;
35
35
  lastCodexValidationStatus?: "ok" | "failed";
36
36
  lastCodexValidationError?: string;
37
+ /** OAuth succeeded while quota was exhausted; never route until deferred validation succeeds. */
38
+ codexValidationPending?: boolean;
39
+ /**
40
+ * Set when the recorded failure is TERMINAL: the OAuth grant itself was revoked or has
41
+ * expired, so no retry can recover it and only a re-login will. It distinguishes a dead
42
+ * credential from a transient warmup or probe failure that may clear on its own.
43
+ *
44
+ * Deliberately a separate optional key rather than a third value in
45
+ * `lastCodexValidationStatus`: `isCredentialRecord` admits only `"ok" | "failed"`, so a
46
+ * record carrying an unrecognized status fails validation and is DROPPED from the store
47
+ * on load. An unknown extra key is carried through untouched instead, which keeps a
48
+ * downgrade from deleting the account entry and its credential.
49
+ *
50
+ * Cleared by `markCodexAccountValidated` and — because it is absent from
51
+ * `preservedValidationMetadata` — by every credential write. A refresh that succeeds
52
+ * disproves "the grant was revoked", so the verdict must not outlive it.
53
+ */
54
+ lastCodexValidationTerminal?: boolean;
37
55
  }
@@ -259,6 +259,21 @@ export type OcxRuntimeRole = "standalone" | "hub" | "client";
259
259
  export interface OcxHubConfig {
260
260
  /** Canonical browser-reachable management origin advertised by a hub. */
261
261
  managementPublicOrigin?: string;
262
+ /**
263
+ * Canonical client-reachable DATA origin of this hub — what a remote machine passes as the
264
+ * positional URL to `ocx connect`, and what `ocx hub invite` prints.
265
+ *
266
+ * Separate from `managementPublicOrigin` because the two are genuinely different sockets on a
267
+ * real deployment: management is a loopback-only ingress published by an HTTPS frontend, while
268
+ * the data listener is bound to the hub's tailnet/LAN address and fronted on its own port
269
+ * (`https://hub.tailnet.ts.net:8443`). Deriving one from the other produced an origin that
270
+ * answered `/readyz` and nothing else.
271
+ *
272
+ * Advisory only: it is the origin the hub ADVERTISES, never a bind address. When omitted,
273
+ * `ocx hub invite` falls back to `http://<hostname>:<port>`, which is correct for a plain
274
+ * tailnet bind with no TLS frontend.
275
+ */
276
+ dataPublicOrigin?: string;
262
277
  /**
263
278
  * Optional management-only listener for a local HTTPS frontend such as Tailscale Serve.
264
279
  * The hostname is deliberately not configurable: when enabled the socket is always bound
@@ -288,6 +303,26 @@ export interface OcxRemoteGuiConfig {
288
303
 
289
304
  export type OcxConnectedClientId = "codex" | "claude";
290
305
 
306
+ /**
307
+ * Redaction policy for management and CLI projections (#3859).
308
+ *
309
+ * `privacy` rather than `dashboard`: `ocx status` and `ocx account` are not the dashboard, and
310
+ * they read the same projections.
311
+ */
312
+ export interface OcxPrivacyConfig {
313
+ /**
314
+ * Mask stored account emails before they leave the proxy. Omitted or `true` is the historical
315
+ * behaviour and the default.
316
+ *
317
+ * Setting this to `false` is a real disclosure decision, not a display preference. Management
318
+ * is not always loopback — under `remoteGui` the unmasked address reaches every management
319
+ * principal that can reach the hub, not only someone sitting at the machine. The default
320
+ * therefore stays masked, and turning it off is an explicit opt-in by the operator who owns
321
+ * those accounts.
322
+ */
323
+ maskEmails?: boolean;
324
+ }
325
+
291
326
  export interface OcxClientConnectionConfig {
292
327
  serverUrl: string;
293
328
  managementUrl: string;
@@ -335,6 +370,8 @@ export interface OcxConfig {
335
370
  remoteGui?: OcxRemoteGuiConfig;
336
371
  /** Remote-hub client state. The admission secret is stored only in service-api-token. */
337
372
  client?: OcxClientConnectionConfig;
373
+ /** Operator-facing redaction policy for management and CLI projections. */
374
+ privacy?: OcxPrivacyConfig;
338
375
  /** Opt in to one identical-turn retry when a Responses completion has no text or tool call. */
339
376
  emptyCompletionRetry?: boolean;
340
377
  /**
@@ -639,13 +676,24 @@ export interface OcxConfig {
639
676
  * surface: every process on the machine can reach it, spend account quota, and consume paid
640
677
  * provider credentials. Off by default; not for multi-tenant hosts.
641
678
  *
642
- * The port is required when enabled and must differ from the proxy port. An OS-assigned port
643
- * would change across restarts, which would break already-running app-servers holding the
644
- * previous `base_url` — the exact symptom #1102 reported and we disproved for token rotation.
679
+ * Two enabled forms:
680
+ *
681
+ * - `{ enabled: true, port: N }` — a distinct port (the #1102 form). N must differ from the
682
+ * proxy port.
683
+ * - `{ enabled: true }` — the "companion" form: bind `127.0.0.1:<proxy port>`. Legal only
684
+ * when `hostname` is a specific non-loopback, non-wildcard address (a tailnet or LAN IP),
685
+ * because otherwise the public socket already owns that loopback address. This is the
686
+ * one-port hub shape: remote clients dial `hostname:port`, local processes dial
687
+ * `127.0.0.1:port`, and every integration that hardcodes `http://127.0.0.1:<proxy port>`
688
+ * keeps working on a hub whose public bind they cannot reach (#4236).
689
+ *
690
+ * Neither form is OS-assigned. A changing port would break already-running app-servers
691
+ * holding the previous `base_url` — the exact symptom #1102 reported and we disproved for
692
+ * token rotation.
645
693
  */
646
694
  unauthenticatedLoopbackListener?:
647
695
  | { enabled: false }
648
- | { enabled: true; port: number };
696
+ | { enabled: true; port?: number };
649
697
  /**
650
698
  * Outbound HTTP(S) proxy URL for provider requests (e.g. "http://user:pass@proxy:8080", or
651
699
  * "${HTTPS_PROXY}"-style env reference). Mirrored into HTTP_PROXY/HTTPS_PROXY at startup when
@@ -724,6 +772,14 @@ export interface OcxConfig {
724
772
  codexAccounts?: CodexAccount[];
725
773
  /** Account ids administratively excluded from future pool selection until resumed. */
726
774
  pausedCodexAccountIds?: string[];
775
+ /**
776
+ * Codex pool selection policy. Absent means no policy, so an existing install rotates exactly
777
+ * as before.
778
+ *
779
+ * Not in `getDefaultConfig()` on purpose — that function carries no optional-feature keys, so
780
+ * absence is the only default state this policy has.
781
+ */
782
+ codexPool?: OcxCodexPoolConfig;
727
783
  /** Opt-in per-account activation of newly reset Codex quota windows. */
728
784
  codexQuotaAutoRefresh?: Record<string, {
729
785
  fiveHour?: boolean;
@@ -801,6 +857,20 @@ export interface OcxConfig {
801
857
  * that work today — on Azure and custom Responses gateways as well, whose limits are unknown.
802
858
  */
803
859
  maxUpstreamBodyBytes?: number;
860
+ /**
861
+ * Opt-in ceiling, in bytes, on a decompressed INBOUND data-plane request body (#3573).
862
+ *
863
+ * Omitted or 0 = the built-in 256 MiB default. The lever exists because a session on the
864
+ * 922k-token opt-in window serializes its full history past that default, and the request
865
+ * that crosses it is Codex's own remote-compaction request — so the session hits 413 on the
866
+ * one operation that would have shrunk it and cannot recover.
867
+ *
868
+ * Bounded on purpose. `resolveInboundBodyLimitBytes()` clamps to
869
+ * [1 MiB, 512 MiB]; an unbounded inbound cap is a memory DoS because the reader materializes
870
+ * the body several times over. The Bun listener's own `maxRequestBodySize` is fixed when the
871
+ * server starts, so raising this takes effect on restart.
872
+ */
873
+ maxInboundBodyBytes?: number;
804
874
  /**
805
875
  * Opt-in Anthropic OAuth PROACTIVE routing (#294). Default OFF.
806
876
  * Sticky session affinity; new sessions may pick lowest known 5h usage.
@@ -1139,6 +1209,25 @@ export interface OcxWebSearchSidecarConfig {
1139
1209
  streamRoutedModelOutput?: boolean;
1140
1210
  }
1141
1211
 
1212
+ /**
1213
+ * Codex account-pool selection policy.
1214
+ *
1215
+ * This is a selection policy, not a block. An excluded account keeps its credential, quota
1216
+ * history, and thread affinity, stays visible on the account surface, and remains reachable by
1217
+ * explicit account selection. Only automatic rotation skips it.
1218
+ */
1219
+ export interface OcxCodexPoolConfig {
1220
+ /**
1221
+ * Plan keys ordinary rotation skips, matched case-insensitively against the plan stored on each
1222
+ * account. Absent or empty means no policy.
1223
+ *
1224
+ * There is no `minimumPlan` counterpart: ranking ChatGPT plans against each other needs a total
1225
+ * ordering this repository does not have, and inventing one would silently drain a tier the
1226
+ * operator never meant to exclude.
1227
+ */
1228
+ excludedPlans?: string[];
1229
+ }
1230
+
1142
1231
  /**
1143
1232
  * Quota-reset notification settings.
1144
1233
  *
@@ -86,6 +86,57 @@ export interface RateLimitRetryPolicy {
86
86
  respectRetryAfter?: boolean;
87
87
  }
88
88
 
89
+ /**
90
+ * Backend ids admitted by `providers.<name>.webSearchBridge.backend`. Only `"ollama"` has a
91
+ * shipped executor; every other id is explicit-only and inert, the same contract the top-level
92
+ * `webSearchSidecar` uses for backends whose executor has not landed. Naming one of them keeps
93
+ * the bridge disarmed rather than silently falling back to a different search provider — in
94
+ * particular it never auto-selects a paid Luna or Exa search.
95
+ */
96
+ export const PROVIDER_WEB_SEARCH_BRIDGE_BACKENDS = [
97
+ "ollama",
98
+ "openai",
99
+ "anthropic",
100
+ "xai",
101
+ "gemini",
102
+ "exa",
103
+ ] as const;
104
+
105
+ export type ProviderWebSearchBridgeBackend = typeof PROVIDER_WEB_SEARCH_BRIDGE_BACKENDS[number];
106
+
107
+ /**
108
+ * Opt-in hosted-web-search bridge for a KEY-auth Responses passthrough provider
109
+ * (`providers.<name>.webSearchBridge`), default OFF (#3761).
110
+ *
111
+ * Codex always declares the hosted `{type:"web_search"}` tool. On the passthrough the proxy
112
+ * treats that as "the destination runs search itself" and relays it unchanged, which is true for
113
+ * the ChatGPT backend and for xAI but false for an OpenAI-shaped key gateway such as Ollama
114
+ * Cloud: the model answers with a `function_call` named `web_search` that nothing executes,
115
+ * and the undeclared-tool guard ends the turn. With this block enabled the proxy intercepts that
116
+ * call, runs the configured search backend itself, feeds the result back upstream, and shows
117
+ * Codex a hosted `web_search_call` cell.
118
+ *
119
+ * Never armed for `authMode: "forward"` (ChatGPT) or for a provider that executes hosted search
120
+ * upstream; see `planPassthroughWebSearchBridge` in `src/web-search/passthrough-bridge.ts`.
121
+ */
122
+ export interface ProviderWebSearchBridgeConfig {
123
+ /** Master switch. Absent or false keeps today's relay-and-fail behavior exactly. */
124
+ enabled?: boolean;
125
+ /** Which executor runs the search. Absent disarms the bridge; there is no implicit default. */
126
+ backend?: ProviderWebSearchBridgeBackend;
127
+ /** Searches executed per turn before the bridge refuses further ones (1..10, default 3). */
128
+ maxSearches?: number;
129
+ /** Per-search deadline in milliseconds (1000..600000, default 60000). */
130
+ timeoutMs?: number;
131
+ /**
132
+ * Absolute search-API URL. Required to use the `ollama` backend against anything other than
133
+ * the canonical `https://ollama.com` origin, which is the only origin derived automatically.
134
+ * The bridge sends the PROVIDER's own API key to this URL, so an operator setting it is
135
+ * authorizing that key for this destination.
136
+ */
137
+ endpoint?: string;
138
+ }
139
+
89
140
  /**
90
141
  * User-configured display price for one model (USD per 1M tokens).
91
142
  * Mirrors the `Cost4` shape used by the usage cost estimator; structurally
@@ -550,6 +601,11 @@ export interface OcxProviderConfig {
550
601
  * SSE/JSON; raw inspection state remains authoritative.
551
602
  */
552
603
  responsesSnapshotRepair?: boolean;
604
+ /**
605
+ * Opt-in hosted-web-search bridge for this KEY-auth Responses passthrough provider (#3761).
606
+ * Absent or disabled leaves the passthrough byte-identical to today.
607
+ */
608
+ webSearchBridge?: ProviderWebSearchBridgeConfig;
553
609
  /**
554
610
  * Provider-wide mapping from Codex effort labels to upstream `reasoning_effort` values.
555
611
  * Map a label to the reserved value `"__omit__"` to send no reasoning field at all for that
package/src/types.ts CHANGED
@@ -101,6 +101,8 @@ export type {
101
101
  ResponsesItemIdRepairConfig,
102
102
  RateLimitRetryPolicy,
103
103
  TransientRetryPolicy,
104
+ ProviderWebSearchBridgeBackend,
105
+ ProviderWebSearchBridgeConfig,
104
106
  ProviderCostOverlay,
105
107
  RequestPacingRule,
106
108
  ProviderRequestPacingConfig,
@@ -111,6 +113,8 @@ export type {
111
113
  OcxProviderConfig,
112
114
  } from "./types/provider";
113
115
 
116
+ export { PROVIDER_WEB_SEARCH_BRIDGE_BACKENDS } from "./types/provider";
117
+
114
118
  export type {
115
119
  CodexAccount,
116
120
  CodexAccountCredentials,
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Cached "is an update available?" answer for the GUI sidebar badge.
3
3
  *
4
- * `/api/update/check` spawns `npm view` on every call (~1s, network-bound), so a
4
+ * `/api/update/check` spawns the installing manager's `view` command on every call
5
+ * (~1s, network-bound), so a
5
6
  * sidebar that polls it would spawn a process per tick on every page of the GUI.
6
7
  * The badge instead READS the 20h version cache the CLI update prompt already
7
8
  * maintains (`~/.opencodex/version.json`).
8
9
  *
9
10
  * This is deliberately read-only: it must never trigger a registry refresh. The GUI
10
- * polls it, so a refresh-on-read would let repeated polls launch repeated `npm view`
11
+ * polls it, so a refresh-on-read would let repeated polls launch repeated manager `view`
11
12
  * helpers with no coalescing. Cache warming stays with `ocx start`
12
13
  * (`triggerBackgroundRefreshIfStale` in `src/update/notify.ts`) and with the explicit
13
14
  * `/api/update/check` the user reaches by clicking the sidebar update button.