@bitkyc08/opencodex 2.52.0 → 2.53.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 (236) hide show
  1. package/gui/dist/assets/index-BBOZWGB6.css +1 -0
  2. package/gui/dist/assets/index-BlO4Yl6q.js +128 -0
  3. package/gui/dist/index.html +2 -2
  4. package/native/remote-workspace-helper/Cargo.lock +130 -0
  5. package/native/remote-workspace-helper/Cargo.toml +24 -0
  6. package/native/remote-workspace-helper/src/main.rs +49 -0
  7. package/native/remote-workspace-helper/src/protocol.rs +246 -0
  8. package/native/remote-workspace-helper/src/sandbox/macos.rs +19 -0
  9. package/native/remote-workspace-helper/src/sandbox/mod.rs +77 -0
  10. package/native/remote-workspace-helper/src/sandbox/windows.rs +15 -0
  11. package/package.json +6 -1
  12. package/src/adapters/anthropic-image-normalize.ts +30 -2
  13. package/src/adapters/anthropic.ts +1 -1
  14. package/src/adapters/base.ts +8 -2
  15. package/src/adapters/cursor/cursor-errors.ts +12 -0
  16. package/src/adapters/cursor/thread-continuity.ts +93 -0
  17. package/src/adapters/cursor.ts +104 -73
  18. package/src/adapters/devin/cloud-direct/chat.ts +312 -23
  19. package/src/adapters/devin/cloud-direct/metadata.ts +31 -2
  20. package/src/adapters/devin/live-models.ts +70 -3
  21. package/src/adapters/devin.ts +281 -21
  22. package/src/adapters/google-wire-compiler.ts +14 -6
  23. package/src/adapters/google.ts +22 -8
  24. package/src/adapters/kiro/adapter.ts +316 -0
  25. package/src/adapters/kiro/conversation.ts +136 -0
  26. package/src/adapters/kiro/payload.ts +432 -0
  27. package/src/adapters/kiro/reasoning.ts +56 -0
  28. package/src/adapters/kiro/stream.ts +1153 -0
  29. package/src/adapters/kiro/usage.ts +223 -0
  30. package/src/adapters/kiro/wire.ts +76 -0
  31. package/src/adapters/kiro.ts +8 -2319
  32. package/src/adapters/mimo-free.ts +1 -1
  33. package/src/adapters/openai-chat-images.ts +101 -0
  34. package/src/adapters/openai-chat.ts +201 -181
  35. package/src/adapters/openai-responses.ts +92 -224
  36. package/src/adapters/registry.ts +0 -7
  37. package/src/adapters/run-turn-queue.ts +13 -6
  38. package/src/bridge.ts +14 -15
  39. package/src/chat/inbound.ts +29 -4
  40. package/src/chat/outbound.ts +145 -107
  41. package/src/claude/desktop-profile.ts +4 -6
  42. package/src/cli/account-api.ts +14 -0
  43. package/src/cli/account-extended.ts +1 -1
  44. package/src/cli/account-history.ts +60 -0
  45. package/src/cli/account-main.ts +80 -0
  46. package/src/cli/account.ts +11 -3
  47. package/src/cli/capabilities.ts +113 -0
  48. package/src/cli/catalog.ts +109 -0
  49. package/src/cli/dispatch.ts +9 -0
  50. package/src/cli/help.ts +2 -0
  51. package/src/cli/index.ts +2 -2
  52. package/src/cli/observe.ts +28 -1
  53. package/src/cli/opencode.ts +42 -8
  54. package/src/cli/provider-runtime.ts +11 -1
  55. package/src/cli/provider.ts +22 -2
  56. package/src/cli/registry.ts +21 -0
  57. package/src/cli/remote-workspace.ts +154 -0
  58. package/src/cli/status.ts +39 -7
  59. package/src/cli/usage-report.ts +14 -2
  60. package/src/client/hub-client.ts +34 -0
  61. package/src/client/hub-state.ts +9 -1
  62. package/src/codex/account-store.ts +78 -0
  63. package/src/codex/auth-api.ts +81 -54
  64. package/src/codex/auth-context.ts +45 -16
  65. package/src/codex/catalog/effort.ts +1 -1
  66. package/src/codex/catalog/metadata.ts +3 -6
  67. package/src/codex/catalog/native-models.ts +4 -4
  68. package/src/codex/catalog/parsing.ts +2 -20
  69. package/src/codex/catalog/provider-fetch.ts +10 -1
  70. package/src/codex/catalog/remote.ts +233 -0
  71. package/src/codex/catalog/sync.ts +403 -35
  72. package/src/codex/convergence.ts +1 -1
  73. package/src/codex/history-manifest.ts +36 -0
  74. package/src/codex/history-provider.ts +32 -5
  75. package/src/codex/inject.ts +9 -0
  76. package/src/codex/main-account.ts +113 -0
  77. package/src/codex/main-device-reauth-api.ts +89 -0
  78. package/src/codex/main-device-reauth.ts +217 -0
  79. package/src/codex/native-residue.ts +9 -2
  80. package/src/codex/quota-auto-refresh.ts +3 -2
  81. package/src/codex/quota-capacity.ts +98 -0
  82. package/src/codex/quota-history.ts +160 -0
  83. package/src/codex/quota-types.ts +8 -0
  84. package/src/codex/quota.ts +118 -91
  85. package/src/codex/refresh.ts +2 -1
  86. package/src/codex/routing.ts +90 -17
  87. package/src/codex/sync.ts +33 -4
  88. package/src/combos/request.ts +19 -1
  89. package/src/config/multi-agent-surface.ts +61 -0
  90. package/src/config/provider-validation.ts +176 -0
  91. package/src/config.ts +213 -11
  92. package/src/generated/compatibility-version.json +436 -168
  93. package/src/images/loop.ts +119 -36
  94. package/src/lib/admission.ts +12 -6
  95. package/src/lib/redact.ts +7 -0
  96. package/src/lib/translator-budget.ts +4 -3
  97. package/src/lib/windows-atomic-replace.ts +1 -0
  98. package/src/lib/windows-elevation.ts +1 -1
  99. package/src/oauth/chatgpt-device.ts +62 -5
  100. package/src/oauth/devin/cli-import.ts +130 -0
  101. package/src/oauth/devin.ts +63 -8
  102. package/src/oauth/index.ts +29 -14
  103. package/src/oauth/kiro.ts +18 -6
  104. package/src/oauth/login-cli.ts +9 -1
  105. package/src/oauth/meta-muse-device.ts +464 -0
  106. package/src/oauth/meta-muse.ts +123 -32
  107. package/src/oauth/pool-kernel.ts +9 -0
  108. package/src/oauth/pool-settings-capability.ts +2 -2
  109. package/src/oauth/store.ts +57 -0
  110. package/src/oauth/types.ts +31 -0
  111. package/src/providers/derive.ts +13 -3
  112. package/src/providers/devin-cli-authmode-migration.ts +57 -35
  113. package/src/providers/devin-provider-merge-migration.ts +240 -0
  114. package/src/providers/muse-key-quota.ts +117 -0
  115. package/src/providers/muse-subscription-usage.ts +14 -2
  116. package/src/providers/openai-sidecar.ts +25 -3
  117. package/src/providers/opencode-zen-rate-limit.ts +58 -0
  118. package/src/providers/provider-id-rewrite.ts +20 -5
  119. package/src/providers/quota-types.ts +12 -0
  120. package/src/providers/quota.ts +143 -102
  121. package/src/providers/reasoning-metadata.ts +543 -0
  122. package/src/providers/registry.ts +80 -49
  123. package/src/reasoning-effort.ts +26 -2
  124. package/src/remote/hub-usage.ts +32 -0
  125. package/src/remote-control/index.ts +192 -41
  126. package/src/remote-control/workspace-activation.ts +9 -0
  127. package/src/remote-control/workspace-agent-connection.ts +366 -0
  128. package/src/remote-control/workspace-claude-runtime.ts +243 -0
  129. package/src/remote-control/workspace-codex-runtime.ts +531 -0
  130. package/src/remote-control/workspace-codex-sandbox.ts +115 -0
  131. package/src/remote-control/workspace-command-runner.ts +748 -0
  132. package/src/remote-control/workspace-coordinator.ts +231 -0
  133. package/src/remote-control/workspace-device.ts +585 -0
  134. package/src/remote-control/workspace-executable.ts +43 -0
  135. package/src/remote-control/workspace-executor.ts +397 -0
  136. package/src/remote-control/workspace-hub.ts +519 -0
  137. package/src/remote-control/workspace-pi-runtime.ts +382 -0
  138. package/src/remote-control/workspace-process.ts +129 -0
  139. package/src/remote-control/workspace-rpc.ts +304 -0
  140. package/src/remote-control/workspace-runtime.ts +60 -0
  141. package/src/remote-control/workspace-secret-store.ts +39 -0
  142. package/src/remote-control/workspace-sessions.ts +799 -0
  143. package/src/remote-control/workspace-tool-bridge.ts +192 -0
  144. package/src/responses/code-mode-helper-compat.ts +22 -3
  145. package/src/responses/hosted-tool-policy.ts +0 -1
  146. package/src/responses/muse-tool-name-alias.ts +379 -0
  147. package/src/responses/plaintext-v2-agent-messages.ts +902 -0
  148. package/src/router.ts +7 -0
  149. package/src/routing/compatibility/behavior.ts +0 -1
  150. package/src/server/audio-client.ts +64 -0
  151. package/src/server/audio-dictation.ts +91 -0
  152. package/src/server/audio-live.ts +185 -0
  153. package/src/server/audio-transcriptions.ts +183 -0
  154. package/src/server/audio-upstream.ts +153 -0
  155. package/src/server/auth-cors.ts +61 -2
  156. package/src/server/chat-completions.ts +1 -1
  157. package/src/server/chat-native-sse.ts +92 -48
  158. package/src/server/chat-native.ts +37 -15
  159. package/src/server/hub-usage.ts +57 -0
  160. package/src/server/images.ts +4 -0
  161. package/src/server/index.ts +722 -57
  162. package/src/server/lifecycle.ts +5 -6
  163. package/src/server/live-call-bindings.ts +60 -0
  164. package/src/server/live.ts +12 -1
  165. package/src/server/management/agent-settings-routes.ts +25 -4
  166. package/src/server/management/api-access.ts +37 -0
  167. package/src/server/management/api-key-usage.ts +7 -2
  168. package/src/server/management/config-routes.ts +1 -18
  169. package/src/server/management/context.ts +15 -0
  170. package/src/server/management/logs-usage-routes.ts +2 -0
  171. package/src/server/management/oauth-account-routes.ts +39 -12
  172. package/src/server/management/provider-routes.ts +125 -2
  173. package/src/server/management/remote-workspace-routes.ts +140 -0
  174. package/src/server/management/route-registry.ts +15 -0
  175. package/src/server/management/usage-aggregate-cache.ts +14 -15
  176. package/src/server/management/usage-summary-cache.ts +2 -0
  177. package/src/server/management-api.ts +23 -0
  178. package/src/server/ports.ts +17 -0
  179. package/src/server/relay-eager.ts +4 -1
  180. package/src/server/relay.ts +70 -10
  181. package/src/server/request-decompress.ts +6 -3
  182. package/src/server/responses/agent-task-recovery.ts +25 -32
  183. package/src/server/responses/codex-auth-error.ts +11 -0
  184. package/src/server/responses/codex-ws-exchange.ts +52 -3
  185. package/src/server/responses/codex-ws-wire.ts +55 -0
  186. package/src/server/responses/compact.ts +9 -1
  187. package/src/server/responses/core.ts +337 -73
  188. package/src/server/responses/encrypted-payload.ts +45 -2
  189. package/src/server/responses/ws-upstream.ts +4 -1
  190. package/src/server/responses-self-named-namespace-scrub.ts +1 -3
  191. package/src/server/responses-undeclared-tool-guard.ts +1 -1
  192. package/src/server/search.ts +3 -0
  193. package/src/server/sse-payload-rewrite.ts +136 -51
  194. package/src/server/ws-bridge.ts +35 -1
  195. package/src/service/cli.ts +372 -0
  196. package/src/service/diagnostics.ts +340 -0
  197. package/src/service/guards.ts +303 -0
  198. package/src/service/health.ts +222 -0
  199. package/src/service/launchd.ts +853 -0
  200. package/src/service/orchestration.ts +617 -0
  201. package/src/service/repair.ts +334 -0
  202. package/src/service/state.ts +363 -0
  203. package/src/service/systemd.ts +229 -0
  204. package/src/service/windows-ops.ts +690 -0
  205. package/src/service/windows-scheduler.ts +769 -0
  206. package/src/service/windows-taskxml.ts +613 -0
  207. package/src/service.ts +22 -5550
  208. package/src/storage/cleanup/db.ts +258 -0
  209. package/src/storage/cleanup/execute.ts +358 -0
  210. package/src/storage/cleanup/paths.ts +189 -0
  211. package/src/storage/cleanup/pending.ts +140 -0
  212. package/src/storage/cleanup/preview.ts +292 -0
  213. package/src/storage/cleanup/reconcile.ts +347 -0
  214. package/src/storage/cleanup/restore.ts +932 -0
  215. package/src/storage/cleanup/satellite.ts +474 -0
  216. package/src/storage/cleanup/staging.ts +129 -0
  217. package/src/storage/cleanup/types.ts +98 -0
  218. package/src/storage/cleanup.ts +49 -3127
  219. package/src/types/accounts.ts +2 -0
  220. package/src/types/config.ts +13 -12
  221. package/src/types/provider.ts +37 -0
  222. package/src/types/request.ts +2 -0
  223. package/src/types/tools.ts +17 -5
  224. package/src/types.ts +1 -0
  225. package/src/usage/expected-prices.ts +127 -0
  226. package/src/usage/log.ts +58 -1
  227. package/src/vision/eligibility.ts +13 -2
  228. package/src/web-search/loop.ts +56 -3
  229. package/gui/dist/assets/index-CWXut3rG.js +0 -115
  230. package/gui/dist/assets/index-EdoPnm9_.css +0 -1
  231. package/src/adapters/devin-cli/acp.ts +0 -204
  232. package/src/adapters/devin-cli/adapter.ts +0 -345
  233. package/src/adapters/devin-cli/binary.ts +0 -69
  234. package/src/adapters/devin-cli/models.ts +0 -57
  235. package/src/oauth/devin-cli.ts +0 -149
  236. package/src/server/responses-reasoning-summary-rewrite.ts +0 -178
@@ -0,0 +1,222 @@
1
+ import { win32 } from "node:path";
2
+ import { loadConfig } from "../config";
3
+ import { serviceApiTokenFilePath } from "../lib/service-secrets";
4
+ import { PROXY_ENV_KEYS } from "../lib/proxy-env";
5
+ import { proxyIdentityAt } from "../server/proxy-liveness";
6
+ import { launchdListenPort } from "./launchd";
7
+ import { serviceLogPath } from "./state";
8
+ import { systemdListenPort } from "./systemd";
9
+ import { windowsListenPort, winswListenPort } from "./windows-ops";
10
+
11
+ export function shellQuote(value: string): string {
12
+ return `'${value.replace(/'/g, "'\\''")}'`;
13
+ }
14
+
15
+ /**
16
+ * Listen port baked into service wrappers / WinSW XML.
17
+ * Priority: explicit override → OCX_BAKE_PORT (update restart) → config.port → 10100.
18
+ * `config.port === 0` means ephemeral for interactive start; services need a stable pin,
19
+ * so treat 0 / invalid like unset (default 10100) instead of baking `--port 0`.
20
+ */
21
+ export function resolveServiceListenPort(override?: number): number {
22
+ if (typeof override === "number" && Number.isFinite(override) && override > 0 && override <= 65535) {
23
+ return Math.trunc(override);
24
+ }
25
+ const baked = process.env.OCX_BAKE_PORT?.trim();
26
+ if (baked && /^\d+$/.test(baked)) {
27
+ const n = Number(baked);
28
+ if (n > 0 && n <= 65535) return n;
29
+ }
30
+ const configured = loadConfig().port;
31
+ if (typeof configured === "number" && configured > 0 && configured <= 65535) return configured;
32
+ return 10100;
33
+ }
34
+
35
+ export function buildServiceShellCommand(bun: string, cli: string, port = resolveServiceListenPort()): string {
36
+ const tokenFile = serviceApiTokenFilePath();
37
+ return `if [ -f ${shellQuote(tokenFile)} ]; then OPENCODEX_API_AUTH_TOKEN="$(cat ${shellQuote(tokenFile)})"; export OPENCODEX_API_AUTH_TOKEN; fi; exec ${shellQuote(bun)} ${shellQuote(cli)} start --port ${port}`;
38
+ }
39
+
40
+ /**
41
+ * The same command shape, launched through a stable `ocx` executable instead of an
42
+ * explicit Bun + CLI pair. The token-file preamble is identical and deliberately shared
43
+ * in form: the service still reads the token from disk at start and never carries it in
44
+ * the unit.
45
+ */
46
+ export function buildServiceLauncherShellCommand(launcher: string, port = resolveServiceListenPort()): string {
47
+ const tokenFile = serviceApiTokenFilePath();
48
+ return `if [ -f ${shellQuote(tokenFile)} ]; then OPENCODEX_API_AUTH_TOKEN="$(cat ${shellQuote(tokenFile)})"; export OPENCODEX_API_AUTH_TOKEN; fi; exec ${shellQuote(launcher)} start --port ${port}`;
49
+ }
50
+
51
+ /**
52
+ * Shared tail parser for the baked `--port <n>`.
53
+ *
54
+ * Terminators cover all three artifact shapes: whitespace (batch wrapper, systemd
55
+ * unit), `"` (systemd's quoted ExecStart), `<` (WinSW's `</arguments>`), and `&` (an
56
+ * XML-escaped quote). Matched LAST because every artifact carries the Bun and CLI
57
+ * paths ahead of the argument, and a path containing the literal must not shadow it.
58
+ */
59
+ export function parseBakedListenPort(read: () => string): number | null {
60
+ try {
61
+ const last = [...read().matchAll(/start --port (\d{1,5})(?:\s|"|&|<|$)/gm)].at(-1);
62
+ if (!last) return null;
63
+ const n = Number(last[1]);
64
+ return n > 0 && n <= 65535 ? n : null;
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * The listen port of the INSTALLED service artifact, falling back to the configured
72
+ * one. Each reader returns null off its own platform, so the chain needs no platform
73
+ * branch — and on Windows both return null, preserving today's behavior.
74
+ */
75
+ export function installedServiceListenPort(): number {
76
+ return launchdListenPort()
77
+ ?? systemdListenPort()
78
+ ?? windowsListenPort()
79
+ ?? winswListenPort()
80
+ ?? resolveServiceListenPort();
81
+ }
82
+
83
+ export const SERVICE_INSTALL_HEALTH_MS = 20_000;
84
+
85
+ /**
86
+ * Windows gets a longer budget because its cold start does more before the
87
+ * listener exists: NTFS ACL hardening and previous-session journal recovery
88
+ * both run first, and #3009 recorded a service that bound a few seconds past
89
+ * the 20s deadline and then stayed healthy. Reporting that as a terminal
90
+ * repair failure is worse than waiting — the caller's fallback is to start a
91
+ * second proxy against a port that is about to be taken.
92
+ */
93
+ export const SERVICE_INSTALL_HEALTH_WINDOWS_MS = 45_000;
94
+
95
+ /** The health budget for the platform this is running on. */
96
+ export function serviceInstallHealthMs(
97
+ platform: NodeJS.Platform = process.platform,
98
+ ): number {
99
+ return platform === "win32" ? SERVICE_INSTALL_HEALTH_WINDOWS_MS : SERVICE_INSTALL_HEALTH_MS;
100
+ }
101
+
102
+ /**
103
+ * Whether a proxy actually answers on the port this install/start just produced.
104
+ *
105
+ * Registration is not service: `launchctl list` reports a job that never bound, and
106
+ * `systemctl is-active` reports a process that bound nothing. Probing is the only
107
+ * thing that answers the question the user is actually asking.
108
+ *
109
+ * Probes the BAKED target rather than resolving one. `findLiveProxy` resolves through
110
+ * pidfile -> runtime-port -> config.port, and a service reinstall has just invalidated
111
+ * the first two while `resolveServiceListenPort` (OCX_BAKE_PORT precedence, config.port
112
+ * === 0 normalization) can disagree with the third.
113
+ *
114
+ * Soft: returns the outcome, never throws; the caller chooses between a checkmark and
115
+ * an actionable warning.
116
+ */
117
+ export async function confirmServiceServing(
118
+ deps: {
119
+ port?: number;
120
+ hostname?: string;
121
+ probe?: (port: number, hostname: string) => Promise<boolean>;
122
+ sleep?: (ms: number) => Promise<void>;
123
+ now?: () => number;
124
+ timeoutMs?: number;
125
+ } = {},
126
+ ): Promise<{ ok: true; port: number } | { ok: false; port: number }> {
127
+ const port = deps.port ?? installedServiceListenPort();
128
+ const hostname = deps.hostname ?? loadConfig().hostname ?? "127.0.0.1";
129
+ const now = deps.now ?? Date.now;
130
+ const sleep = deps.sleep ?? ((ms: number) => new Promise<void>(r => setTimeout(r, ms)));
131
+ const probe = deps.probe ?? (async (p, h) => !!(await proxyIdentityAt(p, { hostname: h })));
132
+ const deadline = now() + (deps.timeoutMs ?? serviceInstallHealthMs());
133
+ let waited = false;
134
+ for (;;) {
135
+ if (await probe(port, hostname)) return { ok: true, port };
136
+ if (now() >= deadline) break;
137
+ await sleep(500);
138
+ waited = true;
139
+ }
140
+ // The probe that ran last started before the deadline, so a service that binds
141
+ // during it is reported as dead (#3009). Knock once more after a short grace
142
+ // before calling it a failure. A zero budget means the caller asked not to
143
+ // wait, so it gets exactly the single probe it asked for and nothing more.
144
+ if (waited) {
145
+ await sleep(500);
146
+ if (await probe(port, hostname)) return { ok: true, port };
147
+ }
148
+ return { ok: false, port };
149
+ }
150
+
151
+ /**
152
+ * Print the outcome of `install` / `start` / `repair` in terms of what the user cares
153
+ * about — is it serving? — instead of whether the manager accepted the registration.
154
+ *
155
+ * Sets `process.exitCode = 1` when nothing answers. That is deliberate: the GUI update
156
+ * worker reads the child's exit status, so a registered-but-silent service now makes it
157
+ * fall back to a direct proxy start rather than reporting a successful update over a
158
+ * dead port.
159
+ */
160
+ export async function reportServiceServing(
161
+ verb: "installed" | "started" | "repaired" | "restarted",
162
+ deps: Parameters<typeof confirmServiceServing>[0] = {},
163
+ ): Promise<void> {
164
+ const healthBudgetMs = deps.timeoutMs ?? serviceInstallHealthMs();
165
+ // Timed here rather than reported from the budget. confirmServiceServing knocks once
166
+ // more after a grace sleep whenever it waited at all, so the real wait is the budget
167
+ // plus that grace — and printing the budget states a number the run did not spend.
168
+ // What the reader is deciding is whether the service was still coming up, which is a
169
+ // judgement about elapsed time (#3009).
170
+ const now = deps.now ?? Date.now;
171
+ const startedAt = now();
172
+ const serving = await confirmServiceServing({ ...deps, timeoutMs: healthBudgetMs });
173
+ const waitedMs = Math.max(0, now() - startedAt);
174
+ if (serving.ok) {
175
+ console.log(`✅ opencodex service ${verb} and serving on port ${serving.port}.`);
176
+ return;
177
+ }
178
+ console.error(
179
+ `⚠️ Service ${verb}, but no proxy answered on port ${serving.port} after `
180
+ + `${Math.round(waitedMs / 1000)}s.\n`
181
+ + ` The manager registered the job; that is not the same as serving.\n`
182
+ + ` Log: ${serviceLogPath()}\n`
183
+ + ` Meanwhile: ocx start (serves in the foreground)`,
184
+ );
185
+ process.exitCode = 1;
186
+ }
187
+
188
+ /**
189
+ * The command that repairs the CURRENTLY INSTALLED backend without switching it.
190
+ *
191
+ * `ocx service repair` reads the recorded backend itself, so it cannot silently switch a
192
+ * WinSW install to Task Scheduler the way a plain `ocx service install` would. A healthy
193
+ * scheduler definition needs no elevation; a stale definition can be re-registered and prompt.
194
+ */
195
+ export function serviceRepairCommand(): string {
196
+ return "ocx service repair";
197
+ }
198
+
199
+ /**
200
+ * Outbound proxy settings the installing shell had, resolved for baking into a service
201
+ * definition.
202
+ *
203
+ * A service manager does not inherit the environment of the shell that installed it, and
204
+ * `ExecStart=/bin/sh -lc` is dash on Ubuntu/WSL — login dash reads `.profile`, not
205
+ * `.bashrc`, which is where proxy exports usually live. So a user who needs a proxy to
206
+ * reach the upstream got a service that dialed direct: the socket was reset, the retry
207
+ * budget drained, and the request surfaced as `502 Provider unreachable` (#2107). The
208
+ * same install driven through `ocx codex-shim` worked, because that path spawns with
209
+ * `{ ...process.env }`.
210
+ *
211
+ * Lower-case variants are honored because curl-style tooling sets them and the runtime's
212
+ * own `applyProxyEnv` already treats both cases as equivalent. Only the canonical
213
+ * upper-case name is baked, so a definition never carries two spellings of one setting.
214
+ */
215
+ export function resolvedProxyEnv(env: NodeJS.ProcessEnv = process.env): { name: string; value: string }[] {
216
+ const resolved: { name: string; value: string }[] = [];
217
+ for (const key of PROXY_ENV_KEYS) {
218
+ const value = env[key]?.trim() || env[key.toLowerCase()]?.trim();
219
+ if (value) resolved.push({ name: key, value });
220
+ }
221
+ return resolved;
222
+ }