@bitkyc08/opencodex 2.57.0 → 2.58.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.
- package/gui/dist/assets/{index-Cz7CLdif.js → index-BbrHOIY0.js} +2 -2
- package/gui/dist/index.html +1 -1
- package/package.json +2 -2
- package/src/adapters/codebuddy/scaffold-guard.ts +5 -4
- package/src/adapters/command-code.ts +13 -4
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +65 -1
- package/src/adapters/cursor/live-transport.ts +5 -1
- package/src/adapters/cursor/protobuf-events.ts +110 -11
- package/src/adapters/cursor/protobuf-request.ts +19 -1
- package/src/adapters/cursor/text-toolcall.ts +230 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/types.ts +5 -0
- package/src/adapters/cursor.ts +55 -5
- package/src/adapters/google-http.ts +38 -13
- package/src/adapters/mimo-free.ts +32 -17
- package/src/adapters/ollama-native.ts +42 -8
- package/src/adapters/openai-responses/passthrough.ts +30 -4
- package/src/adapters/openai-responses/request-strips.ts +43 -0
- package/src/adapters/physical-send.ts +50 -0
- package/src/bridge/response-json.ts +1 -1
- package/src/bridge/sse.ts +1 -1
- package/src/claude/outbound.ts +14 -4
- package/src/cli/config-command.ts +35 -18
- package/src/cli/dispatch.ts +17 -4
- package/src/cli/index.ts +44 -2
- package/src/cli/system-command.ts +70 -1
- package/src/cli/uninstall-client-state.ts +12 -0
- package/src/codex/auth-context.ts +42 -8
- package/src/codex/desktop-switches.ts +145 -0
- package/src/codex/history-job.ts +5 -1
- package/src/codex/history-provider.ts +33 -4
- package/src/codex/history-worker.ts +14 -1
- package/src/codex/inject/remove.ts +145 -7
- package/src/codex/inject/restore.ts +204 -32
- package/src/codex/inject.ts +3 -7
- package/src/codex/loopback-target.ts +9 -0
- package/src/codex/native-profile-startup.ts +64 -20
- package/src/config/atomic-write.ts +83 -8
- package/src/config/schema/config-schema.ts +2 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +132 -68
- package/src/lib/bounded-subprocess.ts +62 -10
- package/src/lib/windows-secret-acl.ts +151 -15
- package/src/lib/windows-user-principal.ts +5 -1
- package/src/providers/derive.ts +6 -0
- package/src/providers/model-discovery.ts +19 -7
- package/src/providers/registry/entries-core.ts +11 -0
- package/src/providers/registry/entries-extended.ts +50 -28
- package/src/providers/registry/model-seeds.ts +67 -17
- package/src/providers/registry/types.ts +9 -0
- package/src/responses/spill-store.ts +17 -0
- package/src/responses/state/body-policy.ts +25 -0
- package/src/responses/state/spill-queue.ts +8 -6
- package/src/responses/state.ts +3 -22
- package/src/router.ts +4 -0
- package/src/server/auth-cors.ts +1 -0
- package/src/server/index/websocket-handler.ts +48 -1
- package/src/server/management/config-routes.ts +27 -5
- package/src/server/models-capabilities.ts +24 -3
- package/src/server/responses/codex-ws-exchange.ts +65 -4
- package/src/server/responses/combo-stream-preflight.ts +68 -5
- package/src/server/responses/core-combo.ts +26 -0
- package/src/server/responses/core-options.ts +3 -0
- package/src/server/responses/fetch-helpers.ts +4 -1
- package/src/server/responses/native-injection-protocol.ts +42 -0
- package/src/server/responses/native-injection-replay.ts +105 -0
- package/src/server/responses/native-injection.ts +242 -0
- package/src/server/responses/native-response-control.ts +56 -0
- package/src/server/responses/native-response-json.ts +14 -0
- package/src/server/responses/native-response-output.ts +37 -0
- package/src/server/responses/native-steering-log.ts +44 -0
- package/src/server/responses/native-steering-policy.ts +49 -0
- package/src/server/responses/native-steering-replay.ts +126 -0
- package/src/server/responses/native-steering-settings.ts +76 -0
- package/src/server/responses/native-steering.ts +400 -0
- package/src/server/responses/native-tool-results.ts +130 -0
- package/src/server/responses/passthrough-delivery.ts +11 -0
- package/src/server/responses/passthrough-dispatch.ts +33 -1
- package/src/server/responses/request-prepare.ts +41 -0
- package/src/server/responses/ws-upstream.ts +21 -1
- package/src/server/stop-teardown.ts +8 -1
- package/src/server/ws-bridge.ts +16 -1
- package/src/service/cli.ts +13 -1
- package/src/types/config.ts +4 -0
- package/src/types/provider.ts +13 -0
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
forgetEphemeralSecretPath,
|
|
18
18
|
hardenSecretPath,
|
|
19
19
|
hardenSecretPathAsync,
|
|
20
|
+
reattributeHardenedSecretPath,
|
|
21
|
+
windowsSecretAclReapPendingForPath,
|
|
20
22
|
} from "../lib/windows-secret-acl";
|
|
21
23
|
import {
|
|
22
24
|
renameAtomicFile,
|
|
@@ -26,6 +28,26 @@ import { getConfigDir } from "./paths";
|
|
|
26
28
|
|
|
27
29
|
let atomicSequence = 0;
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Whether this writer's Windows ACL branch applies.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately a local override rather than the ACL module's
|
|
35
|
+
* `windowsSecretAclApplies()`. That one is flipped by many unrelated suites
|
|
36
|
+
* through `setPlatformForTests("win32")`, and honouring it here would make every
|
|
37
|
+
* such suite start running icacls against config writes on a POSIX runner. The
|
|
38
|
+
* gate a cost fix needs is one only the cost tests can open.
|
|
39
|
+
*/
|
|
40
|
+
let windowsHardeningOverride: boolean | null = null;
|
|
41
|
+
|
|
42
|
+
function windowsHardeningApplies(): boolean {
|
|
43
|
+
return windowsHardeningOverride ?? process.platform === "win32";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Test seam: drive the Windows hardening branch on a POSIX runner. Null restores. */
|
|
47
|
+
export function setWindowsHardeningForTests(enabled: boolean | null): void {
|
|
48
|
+
windowsHardeningOverride = enabled;
|
|
49
|
+
}
|
|
50
|
+
|
|
29
51
|
/** Shared process-wide suffix source for config-owned atomic sibling files. */
|
|
30
52
|
export function nextAtomicTempSequence(): number {
|
|
31
53
|
return ++atomicSequence;
|
|
@@ -114,6 +136,32 @@ function assertPrivateTempDescriptor(path: string, descriptor: number): void {
|
|
|
114
136
|
}
|
|
115
137
|
}
|
|
116
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Carry the harden applied to the empty temp across the content write.
|
|
141
|
+
*
|
|
142
|
+
* The temp is hardened before it holds a byte, and then `atomicWriteFile` hardens
|
|
143
|
+
* it again before the rename. The second call used to be a full icacls sequence
|
|
144
|
+
* rather than a memo hit, because the ACL memo's freshness component is
|
|
145
|
+
* `ctimeNs` and libuv reports that from the NTFS ChangeTime, which a data write
|
|
146
|
+
* moves. So every secret write on Windows applied the same three-step ACL twice:
|
|
147
|
+
* `/grant:r`, `/inheritance:r`, `/remove:g`, plus up to three `/findsid` probes,
|
|
148
|
+
* all of it to arrive at the ACL the file already had.
|
|
149
|
+
*
|
|
150
|
+
* It runs after the descriptor closes, not before, because Windows may not have
|
|
151
|
+
* published the new ChangeTime to a path query while the handle is still open;
|
|
152
|
+
* refreshing to a time the next reader will not see would leave the memo missing
|
|
153
|
+
* and change nothing. What licenses the shortcut is the identity assertion the
|
|
154
|
+
* caller makes immediately before the close, which proves this path resolves to
|
|
155
|
+
* the object the ACL was applied to, plus the object comparison inside
|
|
156
|
+
* `reattributeHardenedSecretPath`, which refuses to move the memo to a different
|
|
157
|
+
* object. Nothing is skipped on the strength of the pathname alone, and a
|
|
158
|
+
* refusal costs only the second full harden this is trying to avoid.
|
|
159
|
+
*/
|
|
160
|
+
function carryHardenAcrossContentWrite(path: string): void {
|
|
161
|
+
if (!windowsHardeningApplies()) return;
|
|
162
|
+
reattributeHardenedSecretPath(path);
|
|
163
|
+
}
|
|
164
|
+
|
|
117
165
|
function writePrivateTempFile(
|
|
118
166
|
path: string,
|
|
119
167
|
content: string,
|
|
@@ -123,16 +171,24 @@ function writePrivateTempFile(
|
|
|
123
171
|
const descriptor = openSync(path, "wx", 0o600);
|
|
124
172
|
onCreated();
|
|
125
173
|
try {
|
|
126
|
-
if (
|
|
174
|
+
if (windowsHardeningApplies()) {
|
|
127
175
|
hardenSecretPath(path, { required: true, timeoutMemoKey });
|
|
128
|
-
}
|
|
176
|
+
}
|
|
177
|
+
// Keyed on the REAL platform, not the override: on a POSIX host the mode is
|
|
178
|
+
// the boundary and `assertPrivateTempDescriptor` demands 0o600, which an
|
|
179
|
+
// ambient umask can otherwise take away from the open above.
|
|
180
|
+
if (process.platform !== "win32") {
|
|
129
181
|
fchmodSync(descriptor, 0o600);
|
|
130
182
|
}
|
|
131
183
|
assertPrivateTempDescriptor(path, descriptor);
|
|
132
184
|
writeFileSync(descriptor, content, { encoding: "utf-8" });
|
|
185
|
+
// Second assertion, after the content write: the object this path resolves
|
|
186
|
+
// to is still the object the ACL was applied to and the one we just wrote.
|
|
187
|
+
assertPrivateTempDescriptor(path, descriptor);
|
|
133
188
|
} finally {
|
|
134
189
|
closeSync(descriptor);
|
|
135
190
|
}
|
|
191
|
+
carryHardenAcrossContentWrite(path);
|
|
136
192
|
}
|
|
137
193
|
|
|
138
194
|
async function writePrivateTempFileAsync(
|
|
@@ -144,16 +200,19 @@ async function writePrivateTempFileAsync(
|
|
|
144
200
|
const descriptor = openSync(path, "wx", 0o600);
|
|
145
201
|
onCreated();
|
|
146
202
|
try {
|
|
147
|
-
if (
|
|
203
|
+
if (windowsHardeningApplies()) {
|
|
148
204
|
await hardenSecretPathAsync(path, { required: true, timeoutMemoKey });
|
|
149
|
-
}
|
|
205
|
+
}
|
|
206
|
+
if (process.platform !== "win32") {
|
|
150
207
|
fchmodSync(descriptor, 0o600);
|
|
151
208
|
}
|
|
152
209
|
assertPrivateTempDescriptor(path, descriptor);
|
|
153
210
|
writeFileSync(descriptor, content, { encoding: "utf-8" });
|
|
211
|
+
assertPrivateTempDescriptor(path, descriptor);
|
|
154
212
|
} finally {
|
|
155
213
|
closeSync(descriptor);
|
|
156
214
|
}
|
|
215
|
+
carryHardenAcrossContentWrite(path);
|
|
157
216
|
}
|
|
158
217
|
|
|
159
218
|
export function atomicWriteFile(
|
|
@@ -171,10 +230,15 @@ export function atomicWriteFile(
|
|
|
171
230
|
const effective: AtomicWriteIO = io ?? {
|
|
172
231
|
write: (tempPath, value) => writePrivateTempFile(tempPath, value, path, () => { ownsTemp = true; }),
|
|
173
232
|
harden: tempPath => {
|
|
174
|
-
|
|
175
|
-
|
|
233
|
+
// No chmod on the Windows branch: there it sets the read-only ATTRIBUTE,
|
|
234
|
+
// which is not the secret boundary and is not what protects this file, and
|
|
235
|
+
// its ChangeTime bump is what used to invalidate the harden memo one line
|
|
236
|
+
// later. On POSIX the mode IS the boundary, so it stays.
|
|
237
|
+
if (windowsHardeningApplies()) {
|
|
176
238
|
hardenSecretPath(tempPath, { required: true, timeoutMemoKey: path });
|
|
239
|
+
return;
|
|
177
240
|
}
|
|
241
|
+
try { chmodSync(tempPath, 0o600); } catch { /* platform may ignore chmod */ }
|
|
178
242
|
},
|
|
179
243
|
rename: renameAtomicFile,
|
|
180
244
|
truncate: tempPath => truncateSync(tempPath, 0),
|
|
@@ -245,10 +309,12 @@ export async function atomicWriteFileAsync(
|
|
|
245
309
|
const effective: AtomicWriteAsyncIO = io ?? {
|
|
246
310
|
write: (tempPath, value) => writePrivateTempFileAsync(tempPath, value, path, () => { ownsTemp = true; }),
|
|
247
311
|
harden: async tempPath => {
|
|
248
|
-
|
|
249
|
-
if (
|
|
312
|
+
// Same reasoning as the synchronous writer above.
|
|
313
|
+
if (windowsHardeningApplies()) {
|
|
250
314
|
await hardenSecretPathAsync(tempPath, { required: true, timeoutMemoKey: path });
|
|
315
|
+
return;
|
|
251
316
|
}
|
|
317
|
+
try { chmodSync(tempPath, 0o600); } catch { /* platform may ignore chmod */ }
|
|
252
318
|
},
|
|
253
319
|
rename: renameAtomicFileAsync,
|
|
254
320
|
truncate: target => truncateSync(target, 0),
|
|
@@ -258,9 +324,11 @@ export async function atomicWriteFileAsync(
|
|
|
258
324
|
assertResolvedTargetAllowed(path, target);
|
|
259
325
|
const tmp = `${target}.ocx.${process.pid}.${nextAtomicTempSequence()}.tmp`;
|
|
260
326
|
let hardened = false;
|
|
327
|
+
let tempWasHardenedBeforeContent = false;
|
|
261
328
|
try {
|
|
262
329
|
if (io) ownsTemp = true;
|
|
263
330
|
await effective.write(tmp, content);
|
|
331
|
+
tempWasHardenedBeforeContent = io === undefined && windowsHardeningApplies();
|
|
264
332
|
await testSeam?.afterTempWrite?.(tmp);
|
|
265
333
|
await effective.harden(tmp);
|
|
266
334
|
hardened = true;
|
|
@@ -268,6 +336,13 @@ export async function atomicWriteFileAsync(
|
|
|
268
336
|
forgetEphemeralSecretPath(tmp);
|
|
269
337
|
} catch (cause) {
|
|
270
338
|
if (!ownsTemp) throw cause;
|
|
339
|
+
// The async ACL belt bounds the writer, but it is not evidence that icacls released this
|
|
340
|
+
// path. Leave the temp in the existing residual state instead of racing an unlink against a
|
|
341
|
+
// live Windows handle. The default Windows writer hardens before writing secret bytes; a
|
|
342
|
+
// failure inside that initial harden leaves its still-empty temp behind.
|
|
343
|
+
if (windowsSecretAclReapPendingForPath(tmp)) {
|
|
344
|
+
throw new AtomicWriteResidualTempError(tmp, tempWasHardenedBeforeContent, { cause });
|
|
345
|
+
}
|
|
271
346
|
let scrubbed = false;
|
|
272
347
|
try {
|
|
273
348
|
await effective.truncate(tmp);
|
|
@@ -59,6 +59,8 @@ import { parseDesktopProfile } from "../../claude/desktop-profile";
|
|
|
59
59
|
import { DEFAULT_APP_OWNED_MEMORY_BUDGET_BYTES, MAX_APP_OWNED_MEMORY_BUDGET_MB, MIN_APP_OWNED_MEMORY_BUDGET_MB } from "../../lib/app-owned-memory";
|
|
60
60
|
|
|
61
61
|
export const configSchema = z.object({
|
|
62
|
+
codexNativeSteering: z.boolean().optional().catch(false),
|
|
63
|
+
codexNativeInjection: z.boolean().optional().catch(false),
|
|
62
64
|
port: z.number().int().min(0).max(65535).default(10100),
|
|
63
65
|
// A malformed hand edit must disable only remote-role behavior, not discard
|
|
64
66
|
// providers or data-plane keys. Live writes are rejected explicitly below.
|
|
@@ -243,6 +243,7 @@ export const providerConfigSchema = z.object({
|
|
|
243
243
|
chatCompletionsPath: z.string().min(1).optional(),
|
|
244
244
|
statelessResponses: z.boolean().optional(),
|
|
245
245
|
requiresAdjacentResponsesToolResults: z.boolean().optional(),
|
|
246
|
+
requiresPairedResponsesToolResults: z.boolean().optional(),
|
|
246
247
|
annotateEmptyToolOutputs: z.boolean().optional(),
|
|
247
248
|
fastWire: fastWireSchema.nullable().optional(),
|
|
248
249
|
supportsServiceTier: z.boolean().optional(),
|