synabun 2026.7.32 β 2026.7.34
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/CHANGELOG.md +38 -0
- package/mcp-server/package-lock.json +2 -2
- package/mcp-server/package.json +1 -1
- package/neural-interface/lib/claude-agent-bridge.js +119 -10
- package/neural-interface/lib/codex-runtime-path.js +102 -35
- package/neural-interface/lib/native-binary-runtime.js +650 -0
- package/neural-interface/lib/native-loop-providers.js +28 -1
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/claude-chat.css +7 -0
- package/neural-interface/public/claude-chat.js +18 -0
- package/neural-interface/public/shared/ui-claude-panel.js +17 -0
- package/neural-interface/scripts/rebuild-pty.js +37 -23
- package/neural-interface/server.js +120 -47
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
# SynaBun v.2026.07.34
|
|
2
|
+
|
|
3
|
+
A reliability release: the Claude sidepanel no longer dies with a misleading "does not match this system's libc" error, and every bundled native binary is checked and repaired at startup.
|
|
4
|
+
|
|
5
|
+
## π Fixed
|
|
6
|
+
|
|
7
|
+
- **"Native binary exists but failed to launch"** β The Claude sidepanel could fail every turn with *"Claude Code native binary at `β¦/claude` exists but failed to launch. This usually means the binary does not match this system's libcβ¦"*. On macOS and Windows that explanation is never right: the real cause is almost always a missing execute bit. npm does not preserve the execute bit for files a package ships without a `bin` entry, which is exactly how the Claude Agent SDK and Codex deliver their native payloads, so a perfectly good binary can land as mode `0644` and fail to spawn. SynaBun now stats the binary the SDK is about to launch, restores the execute bit when it is missing, and retries β and when it cannot repair it, it says what is actually wrong: a permission problem on macOS and Linux, Gatekeeper quarantine (with the `xattr -d com.apple.quarantine` command) on macOS, a genuine libc mismatch only on Linux, and an unlaunchable `.cmd`/`.bat`/`.ps1` shim on Windows.
|
|
8
|
+
- **Only the current machine's binaries were repaired** β The startup repair looked at one directory, `prebuilds/<your platform>-<your arch>`, so a checkout carrying several platform builds left the others broken β including `darwin-x64`, which is what an Intel Mac needs for terminals. Startup and postinstall now sweep every vendored binary that is present (Claude Agent SDK, Codex, `node-pty`) by walking the tree instead of consulting a hardcoded list, so new platform directories are covered automatically.
|
|
9
|
+
- **The postinstall repair had stopped doing anything** β `scripts/rebuild-pty.js` was still fixing permissions on `node_modules/.bin/claude` and `@anthropic-ai/claude-code/cli.js`, neither of which has existed since that package was replaced by the Agent SDK. Its prebuild list named Linux directories that are not shipped while missing ones that are. It now runs the shared sweep, and runs it *before* the "node-pty not found, skipping" guard β previously an install without node-pty skipped the Claude and Codex repair entirely.
|
|
10
|
+
- **A `.cjs` executable override was launched the wrong way** β A custom Claude executable ending in `.cjs` was treated as a script to run through Node, but the SDK spawns it directly, so it needed the execute bit and did not get it.
|
|
11
|
+
|
|
12
|
+
## β¨ New
|
|
13
|
+
|
|
14
|
+
- **Runtime notices in the Claude sidepanel** β A new amber notice sits between grey status text and a red fatal error: something went wrong, was worked around, and the session is continuing in a degraded state. You will see it when the bundled binary is repaired and retried, and when a session falls back to your globally installed Claude CLI. It appears in both the sidepanel and the standalone chat page.
|
|
15
|
+
- **Fallback to your installed Claude CLI** β If the bundled runtime cannot be launched or repaired, sessions and unattended automation loops now fall back to the Claude CLI you have installed rather than failing the turn. Because model aliases and effort levels resolve per-binary, a version difference between your CLI and the bundled runtime is reported alongside the fallback instead of silently changing which model runs.
|
|
16
|
+
|
|
17
|
+
## βοΈ Changed
|
|
18
|
+
|
|
19
|
+
- **A custom Claude executable path is now validated before use** β The `claude-skin` β `sdkExecutable` override was passed straight through to the SDK, which uses it verbatim with no checks of its own. It is now vetted first, and a bare command name such as `claude` is rejected with an explanation: the SDK spawns without a shell and cannot resolve a name from `PATH`, so handing one over produces a worse and more confusing failure than the one being recovered from. Overrides may be either a script entrypoint or a native binary path, and the same rule now applies to automation loops, which previously accepted anything.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# SynaBun v.2026.07.33
|
|
24
|
+
|
|
25
|
+
A Windows release: Codex schedules and sidepanels now find a globally installed Codex runtime instead of failing at launch.
|
|
26
|
+
|
|
27
|
+
## π Fixed
|
|
28
|
+
|
|
29
|
+
- **Codex could not be found on Windows even with the global CLI installed** β SynaBun located the Codex runtime by looking it up on the inherited `PATH`, and the PATH it built for that lookup only added POSIX directories (`~/.local/bin`, `/usr/local/bin`, `~/.npm-global/bin`) that do not exist on Windows. Started from anywhere whose environment did not already carry the npm global bin β a shortcut, a service, a fresh terminal after an install β Codex was invisible and every Codex schedule failed. Discovery no longer depends on `PATH` at all: `@openai/codex` is now located under `npm root -g`, `%APPDATA%\npm\node_modules`, an `npm_config_prefix` override, the `node_modules` directory that contains SynaBun itself, and Node's own install directory. The Windows PATH augmentation also picks up `%APPDATA%\npm`, the configured npm prefix, and Node's directory, deduplicated, so the shell-aware launch paths improve too.
|
|
30
|
+
- **The native Codex executable was missed under current npm layouts** β The resolver looked in exactly one place, `vendor/<target>/codex/codex.exe` inside the platform package resolved through `require.resolve`. Current Codex releases ship the executable at `vendor/<target>/bin/`, and depending on the npm version the platform package sits either beside `@openai/codex` or nested inside its own `node_modules` β and package `exports` can block the resolve entirely. Both vendor layouts and all three package locations are now checked directly, so hoisting differences no longer read as "Codex is not installed".
|
|
31
|
+
- **Windows launched npm's shell shim instead of the real launcher** β `npm install -g` writes both an extensionless `codex` sh shim and a `codex.cmd`, and `where codex` lists the shim first, which Windows cannot spawn. The sidepanel launch path now prefers a `.cmd`, `.exe`, `.bat`, or `.ps1` launcher and keeps the first result only as a fallback for non-npm installations.
|
|
32
|
+
|
|
33
|
+
## βοΈ Improved
|
|
34
|
+
|
|
35
|
+
- **A missing Codex runtime says which problem it is** β The failure notice now distinguishes "the global Codex package is installed but its Windows runtime is missing" from "Codex is not installed globally", and both point at `npm install -g @openai/codex@latest --include=optional`. The platform executable ships as an optional dependency, so an install that skipped optional packages leaves a Codex that looks present and cannot run. Successful resolution logs which layout it came from, and failures log the candidate paths that were checked.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
1
39
|
# SynaBun v.2026.07.32
|
|
2
40
|
|
|
3
41
|
A follow-up to 2026.07.31 that makes updating fast again on a large data home.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-memory-mcp",
|
|
3
|
-
"version": "2026.7.
|
|
3
|
+
"version": "2026.7.34",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "claude-memory-mcp",
|
|
9
|
-
"version": "2026.7.
|
|
9
|
+
"version": "2026.7.34",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@huggingface/transformers": "^3.0.0",
|
package/mcp-server/package.json
CHANGED
|
@@ -20,7 +20,14 @@
|
|
|
20
20
|
// session with no WS bound, so page-refresh reattach is the same object.
|
|
21
21
|
|
|
22
22
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
23
|
-
import { CLAUDE_EFFORT_LEVELS } from './claude-model-catalog.js';
|
|
23
|
+
import { CLAUDE_EFFORT_LEVELS, checkClaudeCliSkew } from './claude-model-catalog.js';
|
|
24
|
+
import {
|
|
25
|
+
claudeRuntime,
|
|
26
|
+
clearNativeBinaryCache,
|
|
27
|
+
isNativeBinaryLaunchFailure,
|
|
28
|
+
planRuntimeRecovery,
|
|
29
|
+
resolveClaudeExecutableOverride,
|
|
30
|
+
} from './native-binary-runtime.js';
|
|
24
31
|
import { randomUUID } from 'node:crypto';
|
|
25
32
|
import { dirname } from 'node:path';
|
|
26
33
|
import { existsSync, readFileSync } from 'node:fs';
|
|
@@ -154,6 +161,12 @@ class ClaudeSession {
|
|
|
154
161
|
this.lastPrompt = null;
|
|
155
162
|
this.stallRetries = 0;
|
|
156
163
|
|
|
164
|
+
// Native-runtime recovery latches. Set here and NOWHERE else β resetting
|
|
165
|
+
// them on query recreation would turn a permanently broken binary into an
|
|
166
|
+
// infinite repair/retry loop.
|
|
167
|
+
this._nativeRepairTried = false;
|
|
168
|
+
this._fallbackBin = null;
|
|
169
|
+
|
|
157
170
|
this.pendingPerms = new Map(); // requestId β { toolName, resolve, input, kind }
|
|
158
171
|
this.alwaysAllowed = new Set();
|
|
159
172
|
|
|
@@ -291,13 +304,34 @@ class ClaudeSession {
|
|
|
291
304
|
if (parentDir && parentDir !== workDir && dirname(parentDir) !== parentDir) {
|
|
292
305
|
options.additionalDirectories = [parentDir];
|
|
293
306
|
}
|
|
294
|
-
// Default to the SDK's bundled CLI (version-matched). An
|
|
295
|
-
//
|
|
307
|
+
// Default to the SDK's bundled CLI (version-matched). An override can be set
|
|
308
|
+
// via cli-config.json β "claude-skin": { "sdkExecutable": "β¦" } and may be
|
|
309
|
+
// either a .js entrypoint (launched via node) or a native binary path.
|
|
310
|
+
//
|
|
311
|
+
// The SDK uses pathToClaudeCodeExecutable verbatim with zero validation, so
|
|
312
|
+
// everything is vetted here first β notably a bare command name, which would
|
|
313
|
+
// ENOENT under the SDK's spawn(shell:false).
|
|
296
314
|
const sdkExec = deps.sdkExecutable;
|
|
297
|
-
|
|
298
|
-
|
|
315
|
+
const override = resolveClaudeExecutableOverride(sdkExec);
|
|
316
|
+
if (override.path) {
|
|
317
|
+
options.pathToClaudeCodeExecutable = override.path;
|
|
299
318
|
} else if (sdkExec) {
|
|
300
|
-
log('ignoring
|
|
319
|
+
log('ignoring sdkExecutable override:', override.reason);
|
|
320
|
+
this.send({ type: 'stderr', text: `Ignoring sdkExecutable override β ${override.reason}` });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!options.pathToClaudeCodeExecutable) {
|
|
324
|
+
if (this._fallbackBin) {
|
|
325
|
+
// A previous turn proved the bundled binary unusable; stay on the CLI we
|
|
326
|
+
// already fell back to rather than re-failing every turn.
|
|
327
|
+
options.pathToClaudeCodeExecutable = this._fallbackBin;
|
|
328
|
+
} else {
|
|
329
|
+
// Called for its side effect only: this stats the binary the SDK is about
|
|
330
|
+
// to resolve and restores the execute bit if it is missing. We
|
|
331
|
+
// deliberately do NOT pass the resolved path on β letting the SDK resolve
|
|
332
|
+
// for itself keeps us correct even if its resolution logic changes.
|
|
333
|
+
claudeRuntime();
|
|
334
|
+
}
|
|
301
335
|
}
|
|
302
336
|
|
|
303
337
|
log(`session create: resume=${this.sessionId || 'none'} model=${this.model || 'default'} effort=${this.effort || 'default'} mode=${this.permissionMode} cwd=${workDir} sdk=${SDK_VERSION}`);
|
|
@@ -327,10 +361,11 @@ class ClaudeSession {
|
|
|
327
361
|
const msg = err?.message || String(err);
|
|
328
362
|
log('pump error:', msg);
|
|
329
363
|
if (/No conversation found/i.test(msg)) { this._recoverLostSession(); return; }
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
this.
|
|
364
|
+
// The SDK cannot tell a permission problem from a libc mismatch: it buckets
|
|
365
|
+
// every spawn error together and always blames musl/glibc. Diagnose it
|
|
366
|
+
// properly, repair what we can, and fall back to the user's CLI.
|
|
367
|
+
if (isNativeBinaryLaunchFailure(err)) { this._recoverNativeRuntime(err); return; }
|
|
368
|
+
this._finishTurnWithError(msg);
|
|
334
369
|
} finally {
|
|
335
370
|
if (this.q === q) this.q = null;
|
|
336
371
|
}
|
|
@@ -384,6 +419,80 @@ class ClaudeSession {
|
|
|
384
419
|
});
|
|
385
420
|
}
|
|
386
421
|
|
|
422
|
+
/**
|
|
423
|
+
* The bundled native CLI failed to launch.
|
|
424
|
+
*
|
|
425
|
+
* The SDK's own message for this always blames a musl/glibc mismatch, which is
|
|
426
|
+
* only ever right on Linux β on macOS and Windows the real cause is almost
|
|
427
|
+
* always a missing execute bit (npm does not preserve it for binaries shipped
|
|
428
|
+
* without a `bin` entry, and this repo commits node_modules).
|
|
429
|
+
*
|
|
430
|
+
* Recovery is: re-stat and repair β retry once β fall back to the user's
|
|
431
|
+
* installed CLI with a visible notice β give up with an accurate diagnosis.
|
|
432
|
+
*/
|
|
433
|
+
_recoverNativeRuntime(err) {
|
|
434
|
+
const prompt = this.lastPrompt;
|
|
435
|
+
// The cached verdict predates the failure; re-stat so a repair performed by
|
|
436
|
+
// another session (or the boot sweep) is visible.
|
|
437
|
+
clearNativeBinaryCache();
|
|
438
|
+
const runtime = claudeRuntime();
|
|
439
|
+
|
|
440
|
+
let fallbackBin = this._fallbackBin;
|
|
441
|
+
if (!fallbackBin) {
|
|
442
|
+
// getClaudeBin()'s last resort is the bare string 'claude'. The SDK spawns
|
|
443
|
+
// with shell:false, so a bare name is a guaranteed ENOENT β the override
|
|
444
|
+
// validator rejects it, leaving fallbackBin null and giving the user a
|
|
445
|
+
// real diagnosis instead of a second, more confusing failure.
|
|
446
|
+
const candidate = resolveClaudeExecutableOverride(deps.getClaudeBin?.());
|
|
447
|
+
if (candidate.ok && candidate.path) fallbackBin = candidate.path;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
const plan = planRuntimeRecovery({
|
|
451
|
+
err,
|
|
452
|
+
runtime,
|
|
453
|
+
fallbackBin,
|
|
454
|
+
alreadyTried: this._nativeRepairTried,
|
|
455
|
+
});
|
|
456
|
+
log(`native runtime recovery: action=${plan.action} state=${runtime.state} kind=${plan.kind}`);
|
|
457
|
+
|
|
458
|
+
if (plan.action === 'retry') {
|
|
459
|
+
this._nativeRepairTried = true;
|
|
460
|
+
this.sendEvent({ type: 'system', subtype: 'runtime_notice', level: 'warn', message: plan.message });
|
|
461
|
+
this._recreateQuery(prompt);
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (plan.action === 'fallback') {
|
|
466
|
+
this._fallbackBin = fallbackBin;
|
|
467
|
+
this.sendEvent({ type: 'system', subtype: 'runtime_notice', level: 'warn', message: plan.message });
|
|
468
|
+
// Report-only: the fallback CLI may be a different version than the SDK
|
|
469
|
+
// was built against, and alias ids like "opus[1m]" resolve per-binary β so
|
|
470
|
+
// a picked model can silently launch as another version.
|
|
471
|
+
this._announceSkew(fallbackBin);
|
|
472
|
+
this._recreateQuery(prompt);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
this._finishTurnWithError(plan.message);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
_announceSkew(bin) {
|
|
480
|
+
Promise.resolve()
|
|
481
|
+
.then(() => checkClaudeCliSkew(bin, { log: () => {} }))
|
|
482
|
+
.then((skew) => {
|
|
483
|
+
if (!skew?.skewed || this.destroyed) return;
|
|
484
|
+
this.sendEvent({
|
|
485
|
+
type: 'system',
|
|
486
|
+
subtype: 'runtime_notice',
|
|
487
|
+
level: 'warn',
|
|
488
|
+
message: `Version skew: your installed Claude CLI is ${skew.discovered}, but the bundled `
|
|
489
|
+
+ `runtime is ${skew.bundled}. Model aliases and effort levels resolve per-binary, so `
|
|
490
|
+
+ 'behaviour may differ until the bundled binary is repaired.',
|
|
491
|
+
});
|
|
492
|
+
})
|
|
493
|
+
.catch(() => {});
|
|
494
|
+
}
|
|
495
|
+
|
|
387
496
|
_pushUserText(text, images) {
|
|
388
497
|
const content = [];
|
|
389
498
|
if (images?.length) {
|
|
@@ -15,6 +15,8 @@ const CODEX_TARGETS = {
|
|
|
15
15
|
},
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
const CODEX_VENDOR_BINARY_DIRS = ['bin', 'codex'];
|
|
19
|
+
|
|
18
20
|
export function codexPlatformTarget(platform = process.platform, arch = process.arch) {
|
|
19
21
|
return CODEX_TARGETS[`${platform}:${arch}`] || null;
|
|
20
22
|
}
|
|
@@ -34,6 +36,19 @@ function uniquePaths(values, pathImpl) {
|
|
|
34
36
|
return result;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Pick a Windows launcher suitable for the sidepanel's shell-aware spawn path.
|
|
41
|
+
* npm lists its extensionless sh shim before the native Windows launcher, so
|
|
42
|
+
* prefer a PATHEXT-style launcher while retaining the first result as a last
|
|
43
|
+
* resort for non-npm installations.
|
|
44
|
+
*/
|
|
45
|
+
export function selectWindowsCodexLauncher(launchers = [], acceptLauncher = () => true) {
|
|
46
|
+
const accepted = uniquePaths(launchers, path.win32).filter(acceptLauncher);
|
|
47
|
+
return accepted.find((launcher) => /\.(cmd|exe|bat|ps1)$/i.test(launcher))
|
|
48
|
+
|| accepted[0]
|
|
49
|
+
|| null;
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
function packageJsonCandidatesForLauncher(launcher, pathImpl) {
|
|
38
53
|
const binDir = pathImpl.dirname(launcher);
|
|
39
54
|
const candidates = [
|
|
@@ -45,11 +60,55 @@ function packageJsonCandidatesForLauncher(launcher, pathImpl) {
|
|
|
45
60
|
return candidates;
|
|
46
61
|
}
|
|
47
62
|
|
|
63
|
+
export function windowsCodexPackageJsonCandidates({
|
|
64
|
+
npmRoots = [],
|
|
65
|
+
appData = '',
|
|
66
|
+
npmConfigPrefix = '',
|
|
67
|
+
synabunPackageRoot = '',
|
|
68
|
+
nodeExecutable = '',
|
|
69
|
+
} = {}) {
|
|
70
|
+
const pathImpl = path.win32;
|
|
71
|
+
const nodeModulesRoots = [...npmRoots];
|
|
72
|
+
if (appData) nodeModulesRoots.push(pathImpl.join(appData, 'npm', 'node_modules'));
|
|
73
|
+
if (npmConfigPrefix) {
|
|
74
|
+
nodeModulesRoots.push(
|
|
75
|
+
pathImpl.basename(npmConfigPrefix).toLowerCase() === 'node_modules'
|
|
76
|
+
? npmConfigPrefix
|
|
77
|
+
: pathImpl.join(npmConfigPrefix, 'node_modules'),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
if (
|
|
81
|
+
synabunPackageRoot
|
|
82
|
+
&& pathImpl.basename(synabunPackageRoot).toLowerCase() === 'synabun'
|
|
83
|
+
&& pathImpl.basename(pathImpl.dirname(synabunPackageRoot)).toLowerCase() === 'node_modules'
|
|
84
|
+
) {
|
|
85
|
+
nodeModulesRoots.push(pathImpl.dirname(synabunPackageRoot));
|
|
86
|
+
}
|
|
87
|
+
if (nodeExecutable) {
|
|
88
|
+
nodeModulesRoots.push(pathImpl.join(pathImpl.dirname(nodeExecutable), 'node_modules'));
|
|
89
|
+
}
|
|
90
|
+
return uniquePaths(
|
|
91
|
+
nodeModulesRoots.map((root) => pathImpl.join(root, '@openai', 'codex', 'package.json')),
|
|
92
|
+
pathImpl,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
48
96
|
function defaultResolvePlatformPackage(codexPackageJsonPath, platformPackage) {
|
|
49
97
|
const req = createRequire(codexPackageJsonPath);
|
|
50
98
|
return req.resolve(`${platformPackage}/package.json`);
|
|
51
99
|
}
|
|
52
100
|
|
|
101
|
+
function vendorBinaryCandidates(packageJsonPath, spec, pathImpl) {
|
|
102
|
+
const packageDir = pathImpl.dirname(packageJsonPath);
|
|
103
|
+
return CODEX_VENDOR_BINARY_DIRS.map((binaryDir) => pathImpl.join(
|
|
104
|
+
packageDir,
|
|
105
|
+
'vendor',
|
|
106
|
+
spec.targetTriple,
|
|
107
|
+
binaryDir,
|
|
108
|
+
spec.binaryName,
|
|
109
|
+
));
|
|
110
|
+
}
|
|
111
|
+
|
|
53
112
|
/**
|
|
54
113
|
* Resolve the spawnable native Codex executable from a trusted global npm
|
|
55
114
|
* installation on Windows. `@openai/codex-sdk` uses child_process.spawn with
|
|
@@ -91,50 +150,58 @@ export function resolveTrustedWindowsCodexBinary({
|
|
|
91
150
|
checked.push(codexPackageJsonPath);
|
|
92
151
|
if (!exists(codexPackageJsonPath)) continue;
|
|
93
152
|
|
|
94
|
-
const localVendorBinary
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
spec.targetTriple,
|
|
98
|
-
'codex',
|
|
99
|
-
spec.binaryName,
|
|
100
|
-
);
|
|
101
|
-
checked.push(localVendorBinary);
|
|
102
|
-
if (exists(localVendorBinary) && acceptBinary(localVendorBinary)) {
|
|
103
|
-
return {
|
|
104
|
-
path: localVendorBinary,
|
|
105
|
-
source: 'global-package-vendor',
|
|
106
|
-
packageJsonPath: codexPackageJsonPath,
|
|
107
|
-
...spec,
|
|
108
|
-
checked,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
const platformPackageJsonPath = resolvePlatformPackage(
|
|
114
|
-
codexPackageJsonPath,
|
|
115
|
-
spec.platformPackage,
|
|
116
|
-
);
|
|
117
|
-
const platformBinary = pathImpl.join(
|
|
118
|
-
pathImpl.dirname(platformPackageJsonPath),
|
|
119
|
-
'vendor',
|
|
120
|
-
spec.targetTriple,
|
|
121
|
-
'codex',
|
|
122
|
-
spec.binaryName,
|
|
123
|
-
);
|
|
124
|
-
checked.push(platformPackageJsonPath, platformBinary);
|
|
125
|
-
if (exists(platformBinary) && acceptBinary(platformBinary)) {
|
|
153
|
+
for (const localVendorBinary of vendorBinaryCandidates(codexPackageJsonPath, spec, pathImpl)) {
|
|
154
|
+
checked.push(localVendorBinary);
|
|
155
|
+
if (exists(localVendorBinary) && acceptBinary(localVendorBinary)) {
|
|
126
156
|
return {
|
|
127
|
-
path:
|
|
128
|
-
source: 'global-
|
|
157
|
+
path: localVendorBinary,
|
|
158
|
+
source: 'global-package-vendor',
|
|
129
159
|
packageJsonPath: codexPackageJsonPath,
|
|
130
|
-
platformPackageJsonPath,
|
|
131
160
|
...spec,
|
|
132
161
|
checked,
|
|
133
162
|
};
|
|
134
163
|
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let resolvedPlatformPackageJsonPath = null;
|
|
167
|
+
try {
|
|
168
|
+
resolvedPlatformPackageJsonPath = resolvePlatformPackage(
|
|
169
|
+
codexPackageJsonPath,
|
|
170
|
+
spec.platformPackage,
|
|
171
|
+
);
|
|
135
172
|
} catch (error) {
|
|
136
173
|
checked.push(`${codexPackageJsonPath} -> ${spec.platformPackage}: ${error.message}`);
|
|
137
174
|
}
|
|
175
|
+
|
|
176
|
+
// npm usually hoists the aliased platform package beside @openai/codex,
|
|
177
|
+
// but some npm versions keep it inside that package's node_modules. Check
|
|
178
|
+
// both layouts directly so package exports/hoisting do not block discovery.
|
|
179
|
+
const platformSegments = spec.platformPackage.split('/');
|
|
180
|
+
const codexPackageDir = pathImpl.dirname(codexPackageJsonPath);
|
|
181
|
+
const globalNodeModules = pathImpl.dirname(pathImpl.dirname(codexPackageDir));
|
|
182
|
+
const platformPackageJsonPaths = uniquePaths([
|
|
183
|
+
resolvedPlatformPackageJsonPath,
|
|
184
|
+
pathImpl.join(globalNodeModules, ...platformSegments, 'package.json'),
|
|
185
|
+
pathImpl.join(codexPackageDir, 'node_modules', ...platformSegments, 'package.json'),
|
|
186
|
+
], pathImpl);
|
|
187
|
+
|
|
188
|
+
for (const platformPackageJsonPath of platformPackageJsonPaths) {
|
|
189
|
+
checked.push(platformPackageJsonPath);
|
|
190
|
+
if (!exists(platformPackageJsonPath)) continue;
|
|
191
|
+
for (const platformBinary of vendorBinaryCandidates(platformPackageJsonPath, spec, pathImpl)) {
|
|
192
|
+
checked.push(platformBinary);
|
|
193
|
+
if (exists(platformBinary) && acceptBinary(platformBinary)) {
|
|
194
|
+
return {
|
|
195
|
+
path: platformBinary,
|
|
196
|
+
source: 'global-platform-package',
|
|
197
|
+
packageJsonPath: codexPackageJsonPath,
|
|
198
|
+
platformPackageJsonPath,
|
|
199
|
+
...spec,
|
|
200
|
+
checked,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
138
205
|
}
|
|
139
206
|
|
|
140
207
|
return {
|