@lavilas/codex 1.3.55 → 1.3.60

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/bin/codex.js CHANGED
@@ -6,65 +6,23 @@ import { chmodSync, existsSync, readdirSync, statSync } from "fs";
6
6
  import { createRequire } from "node:module";
7
7
  import path from "path";
8
8
  import { fileURLToPath } from "url";
9
+ import {
10
+ PLATFORM_PACKAGE_BY_TARGET,
11
+ detectPackageManager,
12
+ getCodexBinaryName,
13
+ resolveTargetTriple,
14
+ selectVendorInstallation,
15
+ updateCommandForPackageManager,
16
+ } from "./platform-resolver.js";
9
17
 
10
18
  // __dirname equivalent in ESM
11
19
  const __filename = fileURLToPath(import.meta.url);
12
20
  const __dirname = path.dirname(__filename);
21
+ const packageDir = path.join(__dirname, "..");
13
22
  const require = createRequire(import.meta.url);
14
23
 
15
- const PLATFORM_PACKAGE_BY_TARGET = {
16
- "x86_64-unknown-linux-musl": "@lavilas/codex-linux-x64",
17
- "aarch64-unknown-linux-musl": "@lavilas/codex-linux-arm64",
18
- "x86_64-apple-darwin": "@lavilas/codex-darwin-x64",
19
- "aarch64-apple-darwin": "@lavilas/codex-darwin-arm64",
20
- "x86_64-pc-windows-msvc": "@lavilas/codex-win32-x64",
21
- "aarch64-pc-windows-msvc": "@lavilas/codex-win32-arm64",
22
- };
23
-
24
24
  const { platform, arch } = process;
25
-
26
- let targetTriple = null;
27
- switch (platform) {
28
- case "linux":
29
- case "android":
30
- switch (arch) {
31
- case "x64":
32
- targetTriple = "x86_64-unknown-linux-musl";
33
- break;
34
- case "arm64":
35
- targetTriple = "aarch64-unknown-linux-musl";
36
- break;
37
- default:
38
- break;
39
- }
40
- break;
41
- case "darwin":
42
- switch (arch) {
43
- case "x64":
44
- targetTriple = "x86_64-apple-darwin";
45
- break;
46
- case "arm64":
47
- targetTriple = "aarch64-apple-darwin";
48
- break;
49
- default:
50
- break;
51
- }
52
- break;
53
- case "win32":
54
- switch (arch) {
55
- case "x64":
56
- targetTriple = "x86_64-pc-windows-msvc";
57
- break;
58
- case "arm64":
59
- targetTriple = "aarch64-pc-windows-msvc";
60
- break;
61
- default:
62
- break;
63
- }
64
- break;
65
- default:
66
- break;
67
- }
25
+ const targetTriple = resolveTargetTriple(platform, arch);
68
26
 
69
27
  if (!targetTriple) {
70
28
  throw new Error(`Unsupported platform: ${platform} (${arch})`);
@@ -75,8 +33,8 @@ if (!platformPackage) {
75
33
  throw new Error(`Unsupported target triple: ${targetTriple}`);
76
34
  }
77
35
 
78
- const codexBinaryName = process.platform === "win32" ? "codex.exe" : "codex";
79
- const localVendorRoot = path.join(__dirname, "..", "vendor");
36
+ const codexBinaryName = getCodexBinaryName(process.platform);
37
+ const localVendorRoot = path.join(packageDir, "vendor");
80
38
  const localBinaryPath = path.join(
81
39
  localVendorRoot,
82
40
  targetTriple,
@@ -84,36 +42,24 @@ const localBinaryPath = path.join(
84
42
  codexBinaryName,
85
43
  );
86
44
 
87
- let vendorRoot;
88
- try {
89
- const packageJsonPath = require.resolve(`${platformPackage}/package.json`);
90
- vendorRoot = path.join(path.dirname(packageJsonPath), "vendor");
91
- } catch {
92
- if (existsSync(localBinaryPath)) {
93
- vendorRoot = localVendorRoot;
94
- } else {
95
- const packageManager = detectPackageManager();
96
- const updateCommand =
97
- packageManager === "bun"
98
- ? "bun install -g @lavilas/codex@latest"
99
- : "npm install -g @lavilas/codex@latest";
100
- throw new Error(
101
- `Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
102
- );
103
- }
104
- }
45
+ const selectedInstallation = selectVendorInstallation({
46
+ packageDir,
47
+ platformPackage,
48
+ targetTriple,
49
+ binaryName: codexBinaryName,
50
+ localVendorRoot,
51
+ requireResolve: (specifier) => require.resolve(specifier),
52
+ });
105
53
 
106
- if (!vendorRoot) {
107
- const packageManager = detectPackageManager();
108
- const updateCommand =
109
- packageManager === "bun"
110
- ? "bun install -g @lavilas/codex@latest"
111
- : "npm install -g @lavilas/codex@latest";
54
+ if (!selectedInstallation) {
55
+ const packageManager = detectPackageManager({ installDir: __dirname });
56
+ const updateCommand = updateCommandForPackageManager(packageManager);
112
57
  throw new Error(
113
58
  `Missing optional dependency ${platformPackage}. Reinstall Codex: ${updateCommand}`,
114
59
  );
115
60
  }
116
61
 
62
+ const vendorRoot = selectedInstallation.vendorRoot;
117
63
  const archRoot = path.join(vendorRoot, targetTriple);
118
64
  const binaryPath = path.join(archRoot, "codex", codexBinaryName);
119
65
 
@@ -133,31 +79,6 @@ function getUpdatedPath(newDirs) {
133
79
  return updatedPath;
134
80
  }
135
81
 
136
- /**
137
- * Use heuristics to detect the package manager that was used to install Codex
138
- * in order to give the user a hint about how to update it.
139
- */
140
- function detectPackageManager() {
141
- const userAgent = process.env.npm_config_user_agent || "";
142
- if (/\bbun\//.test(userAgent)) {
143
- return "bun";
144
- }
145
-
146
- const execPath = process.env.npm_execpath || "";
147
- if (execPath.includes("bun")) {
148
- return "bun";
149
- }
150
-
151
- if (
152
- __dirname.includes(".bun/install/global") ||
153
- __dirname.includes(".bun\\install\\global")
154
- ) {
155
- return "bun";
156
- }
157
-
158
- return userAgent ? "npm" : null;
159
- }
160
-
161
82
  const additionalDirs = [];
162
83
  const pathDir = path.join(archRoot, "path");
163
84
  if (existsSync(pathDir)) {
@@ -167,7 +88,7 @@ const updatedPath = getUpdatedPath(additionalDirs);
167
88
 
168
89
  const env = { ...process.env, PATH: updatedPath };
169
90
  const packageManagerEnvVar =
170
- detectPackageManager() === "bun"
91
+ detectPackageManager({ installDir: __dirname }) === "bun"
171
92
  ? "CODEX_MANAGED_BY_BUN"
172
93
  : "CODEX_MANAGED_BY_NPM";
173
94
  env[packageManagerEnvVar] = "1";
@@ -0,0 +1,303 @@
1
+ import {
2
+ existsSync,
3
+ mkdirSync,
4
+ readFileSync,
5
+ readdirSync,
6
+ statSync,
7
+ writeFileSync,
8
+ } from "node:fs";
9
+ import path from "node:path";
10
+
11
+ export const PLATFORM_PACKAGE_BY_TARGET = {
12
+ "x86_64-unknown-linux-musl": "@lavilas/codex-linux-x64",
13
+ "aarch64-unknown-linux-musl": "@lavilas/codex-linux-arm64",
14
+ "x86_64-apple-darwin": "@lavilas/codex-darwin-x64",
15
+ "aarch64-apple-darwin": "@lavilas/codex-darwin-arm64",
16
+ "x86_64-pc-windows-msvc": "@lavilas/codex-win32-x64",
17
+ "aarch64-pc-windows-msvc": "@lavilas/codex-win32-arm64",
18
+ };
19
+
20
+ export function resolveTargetTriple(
21
+ platformName = process.platform,
22
+ archName = process.arch,
23
+ ) {
24
+ switch (platformName) {
25
+ case "linux":
26
+ case "android":
27
+ switch (archName) {
28
+ case "x64":
29
+ return "x86_64-unknown-linux-musl";
30
+ case "arm64":
31
+ return "aarch64-unknown-linux-musl";
32
+ default:
33
+ return null;
34
+ }
35
+ case "darwin":
36
+ switch (archName) {
37
+ case "x64":
38
+ return "x86_64-apple-darwin";
39
+ case "arm64":
40
+ return "aarch64-apple-darwin";
41
+ default:
42
+ return null;
43
+ }
44
+ case "win32":
45
+ switch (archName) {
46
+ case "x64":
47
+ return "x86_64-pc-windows-msvc";
48
+ case "arm64":
49
+ return "aarch64-pc-windows-msvc";
50
+ default:
51
+ return null;
52
+ }
53
+ default:
54
+ return null;
55
+ }
56
+ }
57
+
58
+ export function getCodexBinaryName(platformName = process.platform) {
59
+ return platformName === "win32" ? "codex.exe" : "codex";
60
+ }
61
+
62
+ export function detectPackageManager(options = {}) {
63
+ const userAgent = options.userAgent ?? process.env.npm_config_user_agent ?? "";
64
+ if (/\bbun\//.test(userAgent)) {
65
+ return "bun";
66
+ }
67
+
68
+ const execPath = options.execPath ?? process.env.npm_execpath ?? "";
69
+ if (execPath.includes("bun")) {
70
+ return "bun";
71
+ }
72
+
73
+ const installDir = options.installDir ?? "";
74
+ if (
75
+ installDir.includes(".bun/install/global") ||
76
+ installDir.includes(".bun\\install\\global")
77
+ ) {
78
+ return "bun";
79
+ }
80
+
81
+ return userAgent ? "npm" : null;
82
+ }
83
+
84
+ export function updateCommandForPackageManager(packageManager) {
85
+ return packageManager === "bun"
86
+ ? "bun install -g @lavilas/codex@latest"
87
+ : "npm install -g @lavilas/codex@latest";
88
+ }
89
+
90
+ function packageNameSegments(packageName) {
91
+ return packageName.split("/");
92
+ }
93
+
94
+ function packageInstallDir(packageDir, platformPackage) {
95
+ return path.join(packageDir, "node_modules", ...packageNameSegments(platformPackage));
96
+ }
97
+
98
+ function readJsonFile(filePath) {
99
+ try {
100
+ if (!existsSync(filePath)) {
101
+ return null;
102
+ }
103
+ return JSON.parse(readFileSync(filePath, "utf8"));
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ function vendorManifestFor(vendorRoot) {
110
+ return readJsonFile(path.join(vendorRoot, "manifest.json"));
111
+ }
112
+
113
+ function validateVendorRoot(candidate, targetTriple, binaryName) {
114
+ const binaryRelativePath = `${targetTriple}/codex/${binaryName}`;
115
+ const binaryPath = path.join(candidate.vendorRoot, binaryRelativePath);
116
+ if (!existsSync(binaryPath)) {
117
+ return { valid: false, reason: `missing ${binaryRelativePath}` };
118
+ }
119
+
120
+ let binaryStat;
121
+ try {
122
+ binaryStat = statSync(binaryPath);
123
+ } catch {
124
+ return { valid: false, reason: `unable to stat ${binaryRelativePath}` };
125
+ }
126
+ if (!binaryStat.isFile() || binaryStat.size <= 0) {
127
+ return { valid: false, reason: `invalid ${binaryRelativePath}` };
128
+ }
129
+
130
+ const manifest = vendorManifestFor(candidate.vendorRoot);
131
+ const expectedBinary = manifest?.files?.[binaryRelativePath];
132
+ if (
133
+ expectedBinary &&
134
+ typeof expectedBinary.size === "number" &&
135
+ expectedBinary.size !== binaryStat.size
136
+ ) {
137
+ return {
138
+ valid: false,
139
+ reason: `size mismatch for ${binaryRelativePath}`,
140
+ };
141
+ }
142
+
143
+ return {
144
+ valid: true,
145
+ binaryPath,
146
+ pathDir: path.join(candidate.vendorRoot, targetTriple, "path"),
147
+ vendorRoot: candidate.vendorRoot,
148
+ source: candidate.source,
149
+ };
150
+ }
151
+
152
+ function pushCandidate(candidates, seen, vendorRoot, source) {
153
+ if (!existsSync(vendorRoot)) {
154
+ return;
155
+ }
156
+ const resolvedRoot = path.resolve(vendorRoot);
157
+ if (seen.has(resolvedRoot)) {
158
+ return;
159
+ }
160
+ seen.add(resolvedRoot);
161
+ candidates.push({ vendorRoot: resolvedRoot, source });
162
+ }
163
+
164
+ export function collectVendorCandidates({
165
+ packageDir,
166
+ platformPackage,
167
+ localVendorRoot = path.join(packageDir, "vendor"),
168
+ requireResolve = null,
169
+ }) {
170
+ const candidates = [];
171
+ const seen = new Set();
172
+
173
+ if (typeof requireResolve === "function") {
174
+ try {
175
+ const packageJsonPath = requireResolve(`${platformPackage}/package.json`);
176
+ pushCandidate(
177
+ candidates,
178
+ seen,
179
+ path.join(path.dirname(packageJsonPath), "vendor"),
180
+ "resolved-platform-package",
181
+ );
182
+ } catch {
183
+ // Ignore and continue with direct filesystem fallbacks.
184
+ }
185
+ }
186
+
187
+ pushCandidate(
188
+ candidates,
189
+ seen,
190
+ path.join(packageInstallDir(packageDir, platformPackage), "vendor"),
191
+ "nested-platform-package",
192
+ );
193
+
194
+ const scopeDir = path.resolve(packageDir, "..");
195
+ if (existsSync(scopeDir)) {
196
+ for (const entry of readdirSync(scopeDir, { withFileTypes: true })) {
197
+ if (!entry.isDirectory() || !entry.name.startsWith(".codex-")) {
198
+ continue;
199
+ }
200
+ pushCandidate(
201
+ candidates,
202
+ seen,
203
+ path.join(scopeDir, entry.name, "node_modules", ...packageNameSegments(platformPackage), "vendor"),
204
+ `staged-platform-package:${entry.name}`,
205
+ );
206
+ }
207
+ }
208
+
209
+ pushCandidate(candidates, seen, localVendorRoot, "local-vendor-root");
210
+
211
+ return candidates;
212
+ }
213
+
214
+ export function selectVendorInstallation({
215
+ packageDir,
216
+ platformPackage,
217
+ targetTriple,
218
+ binaryName,
219
+ localVendorRoot = path.join(packageDir, "vendor"),
220
+ requireResolve = null,
221
+ }) {
222
+ const candidates = collectVendorCandidates({
223
+ packageDir,
224
+ platformPackage,
225
+ localVendorRoot,
226
+ requireResolve,
227
+ });
228
+ for (const candidate of candidates) {
229
+ const result = validateVendorRoot(candidate, targetTriple, binaryName);
230
+ if (result.valid) {
231
+ return result;
232
+ }
233
+ }
234
+ return null;
235
+ }
236
+
237
+ export function buildPlatformPackageMetadata({
238
+ platformPackage,
239
+ version,
240
+ license = "Apache-2.0",
241
+ repository = null,
242
+ packageManager = null,
243
+ }) {
244
+ const metadata = {
245
+ name: platformPackage,
246
+ version,
247
+ license,
248
+ files: ["vendor"],
249
+ };
250
+ if (repository) {
251
+ metadata.repository = repository;
252
+ }
253
+ if (packageManager) {
254
+ metadata.packageManager = packageManager;
255
+ }
256
+
257
+ if (platformPackage.includes("linux")) {
258
+ metadata.os = ["linux"];
259
+ } else if (platformPackage.includes("darwin")) {
260
+ metadata.os = ["darwin"];
261
+ } else if (platformPackage.includes("win32")) {
262
+ metadata.os = ["win32"];
263
+ }
264
+
265
+ if (platformPackage.endsWith("x64")) {
266
+ metadata.cpu = ["x64"];
267
+ } else if (platformPackage.endsWith("arm64")) {
268
+ metadata.cpu = ["arm64"];
269
+ }
270
+
271
+ metadata.engines = { node: ">=16" };
272
+ return metadata;
273
+ }
274
+
275
+ export function ensurePlatformPackageMetadata({
276
+ packageDir,
277
+ platformPackage,
278
+ version,
279
+ license = "Apache-2.0",
280
+ repository = null,
281
+ packageManager = null,
282
+ }) {
283
+ const vendorRoot = path.join(packageDir, "vendor");
284
+ if (!existsSync(vendorRoot)) {
285
+ return null;
286
+ }
287
+
288
+ const packageJsonPath = path.join(packageDir, "package.json");
289
+ if (existsSync(packageJsonPath)) {
290
+ return packageJsonPath;
291
+ }
292
+
293
+ mkdirSync(packageDir, { recursive: true });
294
+ const metadata = buildPlatformPackageMetadata({
295
+ platformPackage,
296
+ version,
297
+ license,
298
+ repository,
299
+ packageManager,
300
+ });
301
+ writeFileSync(packageJsonPath, `${JSON.stringify(metadata, null, 2)}\n`);
302
+ return packageJsonPath;
303
+ }
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { readFileSync } from "node:fs";
5
+ import { createRequire } from "node:module";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import {
9
+ PLATFORM_PACKAGE_BY_TARGET,
10
+ detectPackageManager,
11
+ ensurePlatformPackageMetadata,
12
+ getCodexBinaryName,
13
+ resolveTargetTriple,
14
+ selectVendorInstallation,
15
+ updateCommandForPackageManager,
16
+ } from "./platform-resolver.js";
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+ const packageDir = path.join(__dirname, "..");
21
+ const require = createRequire(import.meta.url);
22
+
23
+ if (!packageDir.includes(`${path.sep}node_modules${path.sep}`)) {
24
+ process.exit(0);
25
+ }
26
+
27
+ const targetTriple = resolveTargetTriple();
28
+ if (!targetTriple) {
29
+ process.exit(0);
30
+ }
31
+
32
+ const platformPackage = PLATFORM_PACKAGE_BY_TARGET[targetTriple];
33
+ if (!platformPackage) {
34
+ process.exit(0);
35
+ }
36
+
37
+ const rootPackageJson = JSON.parse(
38
+ readFileSync(path.join(packageDir, "package.json"), "utf8"),
39
+ );
40
+ ensurePlatformPackageMetadata({
41
+ packageDir: path.join(packageDir, "node_modules", ...platformPackage.split("/")),
42
+ platformPackage,
43
+ version: rootPackageJson.optionalDependencies?.[platformPackage] ?? rootPackageJson.version,
44
+ license: rootPackageJson.license ?? "Apache-2.0",
45
+ repository: rootPackageJson.repository ?? null,
46
+ packageManager: rootPackageJson.packageManager ?? null,
47
+ });
48
+
49
+ const selectedInstallation = selectVendorInstallation({
50
+ packageDir,
51
+ platformPackage,
52
+ targetTriple,
53
+ binaryName: getCodexBinaryName(),
54
+ requireResolve: (specifier) => require.resolve(specifier),
55
+ });
56
+
57
+ if (!selectedInstallation) {
58
+ const packageManager = detectPackageManager({ installDir: __dirname });
59
+ const updateCommand = updateCommandForPackageManager(packageManager);
60
+ console.error(
61
+ `[lavilas/codex] Failed to validate ${platformPackage}. Reinstall Codex: ${updateCommand}`,
62
+ );
63
+ process.exit(1);
64
+ }
65
+
66
+ const result = spawnSync(selectedInstallation.binaryPath, ["--version"], {
67
+ stdio: "pipe",
68
+ encoding: "utf8",
69
+ timeout: 15000,
70
+ });
71
+
72
+ if (result.error) {
73
+ console.error(
74
+ `[lavilas/codex] Native binary validation failed: ${result.error.message}`,
75
+ );
76
+ process.exit(1);
77
+ }
78
+
79
+ if (result.signal) {
80
+ console.error(
81
+ `[lavilas/codex] Native binary exited with signal ${result.signal} during install validation.`,
82
+ );
83
+ process.exit(1);
84
+ }
85
+
86
+ if (result.status !== 0) {
87
+ const details = (result.stderr || result.stdout || "").trim();
88
+ if (details) {
89
+ console.error(details);
90
+ }
91
+ process.exit(result.status ?? 1);
92
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lavilas/codex",
3
- "version": "1.3.55",
3
+ "version": "1.3.60",
4
4
  "license": "Apache-2.0",
5
5
  "bin": {
6
6
  "codex": "bin/codex.js",
@@ -12,6 +12,10 @@
12
12
  "engines": {
13
13
  "node": ">=16"
14
14
  },
15
+ "scripts": {
16
+ "postinstall": "node bin/postinstall.js",
17
+ "test:bin": "node --test bin/platform-resolver.test.js"
18
+ },
15
19
  "files": [
16
20
  "bin"
17
21
  ],
@@ -22,11 +26,11 @@
22
26
  },
23
27
  "packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc",
24
28
  "optionalDependencies": {
25
- "@lavilas/codex-linux-x64": "1.3.55",
26
- "@lavilas/codex-linux-arm64": "1.3.55",
27
- "@lavilas/codex-darwin-x64": "1.3.55",
28
- "@lavilas/codex-darwin-arm64": "1.3.55",
29
- "@lavilas/codex-win32-x64": "1.3.55",
30
- "@lavilas/codex-win32-arm64": "1.3.55"
29
+ "@lavilas/codex-linux-x64": "1.3.60",
30
+ "@lavilas/codex-linux-arm64": "1.3.60",
31
+ "@lavilas/codex-darwin-x64": "1.3.60",
32
+ "@lavilas/codex-darwin-arm64": "1.3.60",
33
+ "@lavilas/codex-win32-x64": "1.3.60",
34
+ "@lavilas/codex-win32-arm64": "1.3.60"
31
35
  }
32
36
  }