@duanluan/codex-plus-plus-launcher 0.1.11 → 0.1.12
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/README.md +4 -3
- package/README_EN.md +4 -3
- package/codex_plus_plus_launcher/__init__.py +1 -1
- package/npm/build-local-binary.cjs +20 -6
- package/npm/i18n.js +4 -0
- package/npm/launcher.js +379 -7
- package/npm/verify-package.cjs +1 -0
- package/package.json +1 -1
- package/upstream-bin/linux-x64/codex-plus-plus +0 -0
- package/upstream-bin/upstream-release.json +16 -16
- package/upstream-bin/win32-x64/codex-plus-plus-manager.exe +0 -0
- package/upstream-bin/win32-x64/codex-plus-plus.exe +0 -0
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Windows 和 macOS 安装后会得到两个入口:
|
|
|
7
7
|
- `Codex++`:静默启动入口,负责启动 Codex 并注入增强功能。
|
|
8
8
|
- `Codex++ 管理工具`:上游 Tauri 控制面板,用于检查、修复、更新和管理增强功能。
|
|
9
9
|
|
|
10
|
-
Linux
|
|
10
|
+
Linux 支持需要先安装 [ilysenko/codex-desktop-linux](https://github.com/ilysenko/codex-desktop-linux)。npm 包会内置上游 Codex++ 静默 launcher,并为 `codex-desktop-linux` 生成一个 `codex.exe` shim,让 Codex++ 可以通过 `--app-path` 启动 Linux 版 Codex Desktop;Linux 暂不内置 Codex++ 管理工具。
|
|
11
11
|
|
|
12
12
|
## 安装
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ npm install -g @duanluan/codex-plus-plus-launcher
|
|
|
19
19
|
|
|
20
20
|
- Windows:复制到 `%LOCALAPPDATA%\Programs\Codex++`,创建桌面和开始菜单的 `Codex++`、`Codex++ 管理工具` 快捷方式。
|
|
21
21
|
- macOS:优先创建 `/Applications/Codex++.app` 和 `/Applications/Codex++ 管理工具.app`,无权限时退到 `~/Applications`。
|
|
22
|
-
- Linux
|
|
22
|
+
- Linux:如果已安装 `codex-desktop-linux`,复制 `codex-plus-plus` 静默 launcher,生成 `codex.exe` shim,并创建 `~/.local/share/applications/codex-plus-plus.desktop`。如果安装 npm 包时还没装 `codex-desktop-linux`,postinstall 会跳过集成且不报错;之后运行 `cxpp install-app` 或 `cxpp repair-app` 即可补装入口。
|
|
23
23
|
|
|
24
24
|
postinstall 只安装入口和快捷方式,不会自动启动 Codex++、管理工具或 Codex 进程。
|
|
25
25
|
|
|
@@ -45,6 +45,7 @@ Windows/npm 路线默认把“更新 Codex++”收敛成“更新这个 wrapper
|
|
|
45
45
|
- `install_root`:已复制 sidecar 和入口的本机目录。
|
|
46
46
|
- `silent_binary_state` / `manager_binary_state`:两个 sidecar 是否已安装。
|
|
47
47
|
- `silent_entrypoint_state` / `manager_entrypoint_state`:两个桌面入口是否已安装。
|
|
48
|
+
- Linux 还会显示 `linux_codex_desktop_state`、`linux_codex_desktop_start` 和 `linux_codex_shim_state`;`manager_binary_state` / `manager_entrypoint_state` 在 Linux 上为 `unsupported`。
|
|
48
49
|
|
|
49
50
|
## 本地开发验证
|
|
50
51
|
|
|
@@ -54,7 +55,7 @@ Windows/npm 路线默认把“更新 Codex++”收敛成“更新这个 wrapper
|
|
|
54
55
|
npm run build:sidecars-local
|
|
55
56
|
```
|
|
56
57
|
|
|
57
|
-
发布包验证要求 `upstream-bin/win32-x64`、`upstream-bin/darwin-x64`、`upstream-bin/darwin-arm64`
|
|
58
|
+
发布包验证要求 `upstream-bin/win32-x64`、`upstream-bin/darwin-x64`、`upstream-bin/darwin-arm64` 都包含上游二进制和图标,`upstream-bin/linux-x64` 包含上游静默 launcher,并包含 `upstream-release.json`:
|
|
58
59
|
|
|
59
60
|
```bash
|
|
60
61
|
npm run prepack
|
package/README_EN.md
CHANGED
|
@@ -7,7 +7,7 @@ Windows and macOS installs provide two entry points:
|
|
|
7
7
|
- `Codex++`: the silent launcher entry.
|
|
8
8
|
- `Codex++ Manager`: the upstream Tauri control panel for checks, repair, updates, and enhancement management.
|
|
9
9
|
|
|
10
|
-
Linux
|
|
10
|
+
Linux support requires [ilysenko/codex-desktop-linux](https://github.com/ilysenko/codex-desktop-linux) to be installed first. The npm package bundles the upstream Codex++ silent launcher and generates a `codex.exe` shim for `codex-desktop-linux`, so Codex++ can launch the Linux Codex Desktop through `--app-path`; Codex++ Manager is not bundled on Linux yet.
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
@@ -19,7 +19,7 @@ The npm install adds the `cxpp`/`codexpp` commands and copies the bundled upstre
|
|
|
19
19
|
|
|
20
20
|
- Windows: copies to `%LOCALAPPDATA%\Programs\Codex++` and creates Desktop and Start Menu shortcuts for `Codex++` and `Codex++ Manager`.
|
|
21
21
|
- macOS: creates `/Applications/Codex++.app` and `/Applications/Codex++ 管理工具.app`, falling back to `~/Applications` if `/Applications` is not writable.
|
|
22
|
-
- Linux:
|
|
22
|
+
- Linux: if `codex-desktop-linux` is installed, copies the `codex-plus-plus` silent launcher, generates a `codex.exe` shim, and creates `~/.local/share/applications/codex-plus-plus.desktop`. If `codex-desktop-linux` is missing during npm install, postinstall skips integration without failing; run `cxpp install-app` or `cxpp repair-app` after installing it.
|
|
23
23
|
|
|
24
24
|
The postinstall step only installs entry points and shortcuts. It does not auto-launch Codex++, the manager UI, or any Codex process.
|
|
25
25
|
|
|
@@ -45,6 +45,7 @@ Important `doctor --json` fields:
|
|
|
45
45
|
- `install_root`: the local sidecar and entrypoint install directory.
|
|
46
46
|
- `silent_binary_state` / `manager_binary_state`: whether each sidecar is installed.
|
|
47
47
|
- `silent_entrypoint_state` / `manager_entrypoint_state`: whether each desktop entry point is installed.
|
|
48
|
+
- Linux also reports `linux_codex_desktop_state`, `linux_codex_desktop_start`, and `linux_codex_shim_state`; `manager_binary_state` / `manager_entrypoint_state` are `unsupported` on Linux.
|
|
48
49
|
|
|
49
50
|
## Local Verification
|
|
50
51
|
|
|
@@ -54,7 +55,7 @@ Build the upstream sidecars for the current platform:
|
|
|
54
55
|
npm run build:sidecars-local
|
|
55
56
|
```
|
|
56
57
|
|
|
57
|
-
Package verification expects `upstream-bin/win32-x64`, `upstream-bin/darwin-x64`, and `upstream-bin/darwin-arm64` to contain the upstream binaries
|
|
58
|
+
Package verification expects `upstream-bin/win32-x64`, `upstream-bin/darwin-x64`, and `upstream-bin/darwin-arm64` to contain the upstream binaries and icons, `upstream-bin/linux-x64` to contain the upstream silent launcher, and `upstream-release.json` to be present:
|
|
58
59
|
|
|
59
60
|
```bash
|
|
60
61
|
npm run prepack
|
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.12"
|
|
@@ -27,6 +27,9 @@ function buildTargetArgs(key) {
|
|
|
27
27
|
if (key === 'darwin-arm64') {
|
|
28
28
|
return ['--target', 'aarch64-apple-darwin'];
|
|
29
29
|
}
|
|
30
|
+
if (key === 'linux-x64') {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
30
33
|
return [];
|
|
31
34
|
}
|
|
32
35
|
|
|
@@ -35,6 +38,10 @@ function cargoTargetForKey(key) {
|
|
|
35
38
|
return args.length ? args[1] : '';
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
function packageBuildArgs(key) {
|
|
42
|
+
return key === 'linux-x64' ? ['-p', 'codex-plus-launcher'] : [];
|
|
43
|
+
}
|
|
44
|
+
|
|
38
45
|
function main() {
|
|
39
46
|
const root = packageRoot();
|
|
40
47
|
const upstreamDir = process.env.CODEXPP_UPSTREAM_DIR || path.join(os.tmpdir(), 'CodexPlusPlus-upstream-build');
|
|
@@ -49,20 +56,27 @@ function main() {
|
|
|
49
56
|
run('git', ['checkout', 'FETCH_HEAD'], { cwd: upstreamDir });
|
|
50
57
|
}
|
|
51
58
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
if (key !== 'linux-x64') {
|
|
60
|
+
run('npm', ['install', '--package-lock=false'], { cwd: path.join(upstreamDir, 'apps', 'codex-plus-manager'), shell: process.platform === 'win32' });
|
|
61
|
+
run('npm', ['run', 'vite:build'], { cwd: path.join(upstreamDir, 'apps', 'codex-plus-manager'), shell: process.platform === 'win32' });
|
|
62
|
+
}
|
|
63
|
+
run('cargo', ['build', '--release', ...buildTargetArgs(key), ...packageBuildArgs(key)], { cwd: upstreamDir });
|
|
55
64
|
|
|
56
65
|
const cargoTarget = cargoTargetForKey(key);
|
|
57
66
|
const targetDir = path.join(upstreamDir, 'target', ...(cargoTarget ? [cargoTarget] : []), 'release');
|
|
58
67
|
fs.mkdirSync(outDir, { recursive: true });
|
|
59
68
|
const exe = key.startsWith('win32') ? '.exe' : '';
|
|
60
69
|
fs.copyFileSync(path.join(targetDir, `codex-plus-plus${exe}`), path.join(outDir, `codex-plus-plus${exe}`));
|
|
61
|
-
|
|
70
|
+
const managerSource = path.join(targetDir, `codex-plus-plus-manager${exe}`);
|
|
71
|
+
if (fs.existsSync(managerSource)) {
|
|
72
|
+
fs.copyFileSync(managerSource, path.join(outDir, `codex-plus-plus-manager${exe}`));
|
|
73
|
+
}
|
|
62
74
|
const iconSource = key.startsWith('darwin')
|
|
63
75
|
? path.join(upstreamDir, 'apps', 'codex-plus-manager', 'src-tauri', 'icons', 'icon.png')
|
|
64
76
|
: path.join(upstreamDir, 'apps', 'codex-plus-manager', 'src-tauri', 'icons', 'icon.ico');
|
|
65
|
-
fs.
|
|
77
|
+
if (fs.existsSync(iconSource)) {
|
|
78
|
+
fs.copyFileSync(iconSource, path.join(outDir, key.startsWith('darwin') ? 'codex-plus-plus.png' : 'codex-plus-plus.ico'));
|
|
79
|
+
}
|
|
66
80
|
|
|
67
81
|
const cargoToml = fs.readFileSync(path.join(upstreamDir, 'Cargo.toml'), 'utf8');
|
|
68
82
|
const versionMatch = cargoToml.match(/^\s*version\s*=\s*"([^"]+)"/m);
|
|
@@ -86,4 +100,4 @@ if (require.main === module) {
|
|
|
86
100
|
}
|
|
87
101
|
}
|
|
88
102
|
|
|
89
|
-
module.exports = { buildTargetArgs, cargoTargetForKey, main, platformKey };
|
|
103
|
+
module.exports = { buildTargetArgs, cargoTargetForKey, main, packageBuildArgs, platformKey };
|
package/npm/i18n.js
CHANGED
|
@@ -3,8 +3,10 @@ const messages = {
|
|
|
3
3
|
missingPython: '缺少命令:python3、python 或 py',
|
|
4
4
|
missingBinary: '缺少随 npm 包安装的 cxpp 二进制程序',
|
|
5
5
|
missingSidecar: '缺少随 npm 包内置的 Codex++ 上游程序',
|
|
6
|
+
missingLinuxCodexDesktop: '未找到 ilysenko/codex-desktop-linux 安装。请先安装 codex-desktop-linux,或设置 CODEXPP_LINUX_CODEX_START 指向 start.sh',
|
|
6
7
|
missingPip: '当前 Python 缺少 pip,请先安装 pip',
|
|
7
8
|
unsupportedPlatform: '当前 Codex++ 桌面版 npm 包暂不支持该平台',
|
|
9
|
+
unsupportedLinuxManager: 'Linux 暂不提供 Codex++ 管理工具;请使用 cxpp launch 启动 Codex++',
|
|
8
10
|
installingWrapper: '正在安装 codex-plus-plus-launcher',
|
|
9
11
|
installingCodexPlusPlus: '正在安装 Codex++',
|
|
10
12
|
installDone: '安装完成',
|
|
@@ -21,8 +23,10 @@ const messages = {
|
|
|
21
23
|
missingPython: 'missing command: python3, python, or py',
|
|
22
24
|
missingBinary: 'missing bundled cxpp binary installed by the npm package',
|
|
23
25
|
missingSidecar: 'missing bundled upstream Codex++ program installed by the npm package',
|
|
26
|
+
missingLinuxCodexDesktop: 'could not find an ilysenko/codex-desktop-linux install; install codex-desktop-linux first or set CODEXPP_LINUX_CODEX_START to start.sh',
|
|
24
27
|
missingPip: 'pip is not available for the selected Python runtime',
|
|
25
28
|
unsupportedPlatform: 'the bundled Codex++ desktop npm package does not support this platform yet',
|
|
29
|
+
unsupportedLinuxManager: 'Codex++ Manager is not bundled for Linux yet; use cxpp launch to start Codex++',
|
|
26
30
|
installingWrapper: 'installing codex-plus-plus-launcher',
|
|
27
31
|
installingCodexPlusPlus: 'installing Codex++',
|
|
28
32
|
installDone: 'installation completed',
|
package/npm/launcher.js
CHANGED
|
@@ -5,11 +5,14 @@ const readline = require('node:readline');
|
|
|
5
5
|
const { spawn, spawnSync } = require('node:child_process');
|
|
6
6
|
const { t } = require('./i18n.js');
|
|
7
7
|
|
|
8
|
-
const SUPPORTED_PLATFORMS = new Set(['win32-x64', 'darwin-x64', 'darwin-arm64']);
|
|
8
|
+
const SUPPORTED_PLATFORMS = new Set(['win32-x64', 'darwin-x64', 'darwin-arm64', 'linux-x64']);
|
|
9
9
|
const SILENT_BINARY = 'codex-plus-plus';
|
|
10
10
|
const MANAGER_BINARY = 'codex-plus-plus-manager';
|
|
11
11
|
const SILENT_NAME = 'Codex++';
|
|
12
12
|
const MANAGER_NAME = 'Codex++ 管理工具';
|
|
13
|
+
const LINUX_SHIM_DIR_NAME = 'codex-desktop-linux-shim';
|
|
14
|
+
const LINUX_SHIM_BINARY = 'codex.exe';
|
|
15
|
+
const LINUX_DESKTOP_ENTRY = 'codex-plus-plus.desktop';
|
|
13
16
|
|
|
14
17
|
function optionValue(options, key, fallback) {
|
|
15
18
|
const value = options[key];
|
|
@@ -39,6 +42,15 @@ function executableName(name, platform = process.platform) {
|
|
|
39
42
|
return platform === 'win32' ? `${name}.exe` : name;
|
|
40
43
|
}
|
|
41
44
|
|
|
45
|
+
function requiredSidecarKinds(options = {}) {
|
|
46
|
+
const platform = optionValue(options, 'platform', process.platform);
|
|
47
|
+
return platform === 'linux' ? ['silent'] : ['silent', 'manager'];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function sidecarKindSupported(kind, options = {}) {
|
|
51
|
+
return requiredSidecarKinds(options).includes(kind);
|
|
52
|
+
}
|
|
53
|
+
|
|
42
54
|
function upstreamBinDir(options = {}) {
|
|
43
55
|
const root = optionValue(options, 'packageRoot', packageRoot);
|
|
44
56
|
const platform = optionValue(options, 'platform', process.platform);
|
|
@@ -107,7 +119,7 @@ function assertSupportedPlatform(options = {}) {
|
|
|
107
119
|
function assertBundledSidecars(options = {}) {
|
|
108
120
|
const fsImpl = options.fs || fs;
|
|
109
121
|
assertSupportedPlatform(options);
|
|
110
|
-
const missing =
|
|
122
|
+
const missing = requiredSidecarKinds(options).map((kind) => bundledSidecarPath(kind, options)).filter((candidate) => !fsImpl.existsSync(candidate));
|
|
111
123
|
if (missing.length > 0) {
|
|
112
124
|
const error = new Error(`${t('missingSidecar', options.env || process.env)}: ${missing.join(', ')}`);
|
|
113
125
|
error.code = 'CODEXPP_MISSING_SIDECAR';
|
|
@@ -136,6 +148,16 @@ function fallbackMacInstallRoot(options = {}) {
|
|
|
136
148
|
return optionValue(options, 'fallbackInstallRoot', () => path.join(optionValue(options, 'homeDir', () => os.homedir()), 'Applications'));
|
|
137
149
|
}
|
|
138
150
|
|
|
151
|
+
function xdgDataHome(options = {}) {
|
|
152
|
+
const env = options.env || process.env;
|
|
153
|
+
return env.XDG_DATA_HOME || path.join(optionValue(options, 'homeDir', () => os.homedir()), '.local', 'share');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function xdgStateHome(options = {}) {
|
|
157
|
+
const env = options.env || process.env;
|
|
158
|
+
return env.XDG_STATE_HOME || path.join(optionValue(options, 'homeDir', () => os.homedir()), '.local', 'state');
|
|
159
|
+
}
|
|
160
|
+
|
|
139
161
|
function installedSidecarPath(kind, options = {}) {
|
|
140
162
|
const platform = optionValue(options, 'platform', process.platform);
|
|
141
163
|
const root = optionValue(options, 'installRoot', () => detectedInstallRoot(options));
|
|
@@ -162,6 +184,18 @@ function detectedInstallRoot(options = {}) {
|
|
|
162
184
|
return requested;
|
|
163
185
|
}
|
|
164
186
|
|
|
187
|
+
function linuxShimRoot(options = {}) {
|
|
188
|
+
return optionValue(options, 'linuxShimRoot', () => path.join(xdgDataHome(options), 'Codex++', LINUX_SHIM_DIR_NAME));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function linuxEntrypointPath(options = {}) {
|
|
192
|
+
return optionValue(options, 'linuxEntrypointPath', () => path.join(xdgDataHome(options), 'applications', LINUX_DESKTOP_ENTRY));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function linuxInstallStatePath(options = {}) {
|
|
196
|
+
return optionValue(options, 'linuxInstallStatePath', () => path.join(xdgStateHome(options), 'codex-plus-plus-launcher', 'linux-install.json'));
|
|
197
|
+
}
|
|
198
|
+
|
|
165
199
|
function isPermissionError(error) {
|
|
166
200
|
return error && (error.code === 'EPERM' || error.code === 'EACCES');
|
|
167
201
|
}
|
|
@@ -425,7 +459,7 @@ async function installSidecars(options = {}) {
|
|
|
425
459
|
const installRoot = installSidecarRoot(options);
|
|
426
460
|
fsImpl.mkdirSync(installRoot, { recursive: true });
|
|
427
461
|
const installed = {};
|
|
428
|
-
for (const kind of
|
|
462
|
+
for (const kind of requiredSidecarKinds(options)) {
|
|
429
463
|
const source = bundledSidecarPath(kind, options);
|
|
430
464
|
const target = installedSidecarPath(kind, { ...options, installRoot });
|
|
431
465
|
await copyReplacingChangedFile(source, target, { ...options, target });
|
|
@@ -441,6 +475,293 @@ async function installSidecars(options = {}) {
|
|
|
441
475
|
return installed;
|
|
442
476
|
}
|
|
443
477
|
|
|
478
|
+
function normalizeExecutablePath(candidate) {
|
|
479
|
+
return path.resolve(String(candidate || ''));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function linuxStartScriptFromBinary(binaryPath, options = {}) {
|
|
483
|
+
const fsImpl = options.fs || fs;
|
|
484
|
+
const normalized = normalizeExecutablePath(binaryPath);
|
|
485
|
+
const basename = path.basename(normalized);
|
|
486
|
+
const parent = path.dirname(normalized);
|
|
487
|
+
const candidates = [];
|
|
488
|
+
|
|
489
|
+
if (basename === 'start.sh') {
|
|
490
|
+
candidates.push(normalized);
|
|
491
|
+
}
|
|
492
|
+
candidates.push(path.join(parent, 'start.sh'));
|
|
493
|
+
if (basename === 'codex-desktop') {
|
|
494
|
+
candidates.push('/opt/codex-desktop/start.sh');
|
|
495
|
+
candidates.push(path.join(parent, '..', 'opt', 'codex-desktop', 'start.sh'));
|
|
496
|
+
candidates.push(path.join(parent, '..', 'opt', 'codex-desktop-linux', 'lib', 'codex-desktop-linux', 'codex-app', 'start.sh'));
|
|
497
|
+
}
|
|
498
|
+
if (normalized.includes(`${path.sep}codex-desktop-linux${path.sep}`) || normalized.includes('/codex-desktop-linux/')) {
|
|
499
|
+
candidates.push(path.join(parent, '..', 'lib', 'codex-desktop-linux', 'codex-app', 'start.sh'));
|
|
500
|
+
candidates.push(path.join(parent, '..', 'codex-app', 'start.sh'));
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
for (const candidate of candidates) {
|
|
504
|
+
const resolved = path.resolve(candidate);
|
|
505
|
+
if (fsImpl.existsSync(resolved)) {
|
|
506
|
+
return resolved;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function linuxStartScriptCandidates(options = {}) {
|
|
513
|
+
const env = options.env || process.env;
|
|
514
|
+
const homeDir = optionValue(options, 'homeDir', () => os.homedir());
|
|
515
|
+
const candidates = [];
|
|
516
|
+
for (const value of [env.CODEXPP_LINUX_CODEX_START, env.CODEX_DESKTOP_LINUX_START, env.CODEX_DESKTOP_START]) {
|
|
517
|
+
if (value) {
|
|
518
|
+
candidates.push(value);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
for (const value of [env.CODEXPP_LINUX_APPDIR, env.CODEX_DESKTOP_LINUX_APPDIR, env.APPDIR]) {
|
|
522
|
+
if (value) {
|
|
523
|
+
candidates.push(path.join(value, 'opt', 'codex-desktop', 'start.sh'));
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
for (const value of [env.CODEXPP_LINUX_CODEX_BIN, env.CODEX_DESKTOP_LINUX_BIN]) {
|
|
527
|
+
if (value) {
|
|
528
|
+
const start = linuxStartScriptFromBinary(value, options);
|
|
529
|
+
if (start) {
|
|
530
|
+
candidates.push(start);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const which = options.which || ((name) => {
|
|
535
|
+
const result = (options.spawnSync || spawnSync)('which', [name], { encoding: 'utf8' });
|
|
536
|
+
return result.status === 0 ? String(result.stdout || '').trim().split(/\r?\n/)[0] : '';
|
|
537
|
+
});
|
|
538
|
+
for (const name of ['codex-desktop', 'openai-codex-desktop']) {
|
|
539
|
+
try {
|
|
540
|
+
const found = which(name);
|
|
541
|
+
if (found) {
|
|
542
|
+
const start = linuxStartScriptFromBinary(found, options);
|
|
543
|
+
if (start) {
|
|
544
|
+
candidates.push(start);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
} catch (_error) {}
|
|
548
|
+
}
|
|
549
|
+
candidates.push('/opt/codex-desktop/start.sh');
|
|
550
|
+
candidates.push(path.join(homeDir, '.local', 'opt', 'codex-desktop-linux', 'lib', 'codex-desktop-linux', 'codex-app', 'start.sh'));
|
|
551
|
+
candidates.push(path.join(homeDir, 'codex-desktop-linux', 'codex-app', 'start.sh'));
|
|
552
|
+
|
|
553
|
+
const unique = [];
|
|
554
|
+
const seen = new Set();
|
|
555
|
+
for (const candidate of candidates) {
|
|
556
|
+
if (!candidate) {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
const resolved = path.resolve(candidate);
|
|
560
|
+
if (!seen.has(resolved)) {
|
|
561
|
+
seen.add(resolved);
|
|
562
|
+
unique.push(resolved);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return unique;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function detectLinuxCodexDesktop(options = {}) {
|
|
569
|
+
const fsImpl = options.fs || fs;
|
|
570
|
+
for (const startScript of linuxStartScriptCandidates(options)) {
|
|
571
|
+
if (fsImpl.existsSync(startScript)) {
|
|
572
|
+
const appRoot = path.dirname(startScript);
|
|
573
|
+
return {
|
|
574
|
+
kind: 'codex-desktop-linux',
|
|
575
|
+
startScript,
|
|
576
|
+
appRoot,
|
|
577
|
+
resourcesDir: path.join(appRoot, 'resources'),
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return null;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function shellSingleQuote(value) {
|
|
585
|
+
return `'${String(value).replace(/'/g, "'\\''")}'`;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function linuxCodexShimScript(startScript) {
|
|
589
|
+
return [
|
|
590
|
+
'#!/usr/bin/env bash',
|
|
591
|
+
'set -euo pipefail',
|
|
592
|
+
`exec ${shellSingleQuote(startScript)} -- "$@"`,
|
|
593
|
+
'',
|
|
594
|
+
].join('\n');
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function writeLinuxCodexShim(installation, options = {}) {
|
|
598
|
+
const fsImpl = options.fs || fs;
|
|
599
|
+
const shimRoot = linuxShimRoot(options);
|
|
600
|
+
fsImpl.mkdirSync(shimRoot, { recursive: true });
|
|
601
|
+
const shimPath = path.join(shimRoot, LINUX_SHIM_BINARY);
|
|
602
|
+
fsImpl.writeFileSync(shimPath, linuxCodexShimScript(installation.startScript), 'utf8');
|
|
603
|
+
fsImpl.chmodSync(shimPath, 0o755);
|
|
604
|
+
return { shimRoot, shimPath };
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
function linuxDesktopEntry({ name, execPath, iconPath, comment }) {
|
|
608
|
+
const iconLine = iconPath ? `Icon=${iconPath}\n` : '';
|
|
609
|
+
return `[Desktop Entry]
|
|
610
|
+
Name=${name}
|
|
611
|
+
Comment=${comment}
|
|
612
|
+
Exec=${execPath}
|
|
613
|
+
${iconLine}Terminal=false
|
|
614
|
+
Type=Application
|
|
615
|
+
Categories=Development;
|
|
616
|
+
StartupNotify=true
|
|
617
|
+
`;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function installLinuxEntrypoints(installed, options = {}) {
|
|
621
|
+
const fsImpl = options.fs || fs;
|
|
622
|
+
const desktopPath = linuxEntrypointPath(options);
|
|
623
|
+
fsImpl.mkdirSync(path.dirname(desktopPath), { recursive: true });
|
|
624
|
+
const command = `${desktopExecArg(installed.silent)} --app-path ${desktopExecArg(installed.linuxShimRoot)}`;
|
|
625
|
+
fsImpl.writeFileSync(
|
|
626
|
+
desktopPath,
|
|
627
|
+
linuxDesktopEntry({
|
|
628
|
+
name: SILENT_NAME,
|
|
629
|
+
execPath: command,
|
|
630
|
+
iconPath: installed.icon || '',
|
|
631
|
+
comment: 'Launch Codex++ with Codex Desktop for Linux',
|
|
632
|
+
}),
|
|
633
|
+
'utf8',
|
|
634
|
+
);
|
|
635
|
+
return {
|
|
636
|
+
silent: desktopPath,
|
|
637
|
+
manager: 'unsupported',
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function desktopExecArg(value) {
|
|
642
|
+
const raw = String(value);
|
|
643
|
+
if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(raw)) {
|
|
644
|
+
return raw.replace(/%/g, '%%');
|
|
645
|
+
}
|
|
646
|
+
return `"${raw.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/`/g, '\\`').replace(/\$/g, '\\$').replace(/%/g, '%%')}"`;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function linuxShellArg(value) {
|
|
650
|
+
const raw = String(value);
|
|
651
|
+
if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(raw)) {
|
|
652
|
+
return raw;
|
|
653
|
+
}
|
|
654
|
+
return shellSingleQuote(raw);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function linuxHasExplicitAppPath(args) {
|
|
658
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
659
|
+
if (args[index] === '--app-path' && args[index + 1]) {
|
|
660
|
+
return true;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return false;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function linuxShimPathFromRoot(root) {
|
|
667
|
+
return path.join(root, LINUX_SHIM_BINARY);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function linuxStateShimRoots(state) {
|
|
671
|
+
const roots = [];
|
|
672
|
+
if (state && state.codex_shim_root) {
|
|
673
|
+
roots.push(state.codex_shim_root);
|
|
674
|
+
}
|
|
675
|
+
if (state && state.codex_shim) {
|
|
676
|
+
roots.push(path.dirname(state.codex_shim));
|
|
677
|
+
}
|
|
678
|
+
return roots;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function ensureLinuxCodexShimRoot(options = {}) {
|
|
682
|
+
const fsImpl = options.fs || fs;
|
|
683
|
+
const state = readJsonFile(linuxInstallStatePath(options), fsImpl) || {};
|
|
684
|
+
const roots = [...linuxStateShimRoots(state), linuxShimRoot(options)];
|
|
685
|
+
const seen = new Set();
|
|
686
|
+
for (const root of roots) {
|
|
687
|
+
if (!root || seen.has(root)) {
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
seen.add(root);
|
|
691
|
+
if (fsImpl.existsSync(linuxShimPathFromRoot(root))) {
|
|
692
|
+
return root;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
const installation = detectLinuxCodexDesktop(options);
|
|
697
|
+
if (!installation) {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
const shim = writeLinuxCodexShim(installation, options);
|
|
701
|
+
const installedSilent = installedSidecarPath('silent', options);
|
|
702
|
+
const silent = fsImpl.existsSync(installedSilent) ? installedSilent : bundledSidecarPath('silent', options);
|
|
703
|
+
writeLinuxInstallState(
|
|
704
|
+
{
|
|
705
|
+
silent,
|
|
706
|
+
installRoot: path.dirname(silent),
|
|
707
|
+
linuxShim: shim.shimPath,
|
|
708
|
+
linuxShimRoot: shim.shimRoot,
|
|
709
|
+
},
|
|
710
|
+
installation,
|
|
711
|
+
options,
|
|
712
|
+
);
|
|
713
|
+
return shim.shimRoot;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function linuxSilentLaunchArgs(args, options = {}) {
|
|
717
|
+
if (linuxHasExplicitAppPath(args)) {
|
|
718
|
+
return args;
|
|
719
|
+
}
|
|
720
|
+
const shimRoot = ensureLinuxCodexShimRoot(options);
|
|
721
|
+
if (!shimRoot) {
|
|
722
|
+
const error = new Error(t('missingLinuxCodexDesktop', options.env || process.env));
|
|
723
|
+
error.code = 'CODEXPP_MISSING_LINUX_CODEX_DESKTOP';
|
|
724
|
+
throw error;
|
|
725
|
+
}
|
|
726
|
+
return ['--app-path', shimRoot, ...args];
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function writeLinuxInstallState(installed, installation, options = {}) {
|
|
730
|
+
const fsImpl = options.fs || fs;
|
|
731
|
+
const statePath = linuxInstallStatePath(options);
|
|
732
|
+
fsImpl.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
733
|
+
const payload = {
|
|
734
|
+
mode: 'codex_desktop_linux',
|
|
735
|
+
app_integration_state: 'installed',
|
|
736
|
+
codex_desktop_linux_start: installation.startScript,
|
|
737
|
+
codex_desktop_linux_app_root: installation.appRoot,
|
|
738
|
+
codex_shim: installed.linuxShim,
|
|
739
|
+
codex_shim_root: installed.linuxShimRoot,
|
|
740
|
+
silent_binary: installed.silent,
|
|
741
|
+
upstream_version: bundledUpstreamVersion(options) || null,
|
|
742
|
+
installed_at: new Date().toISOString(),
|
|
743
|
+
};
|
|
744
|
+
fsImpl.writeFileSync(statePath, JSON.stringify(payload, null, 2) + '\n', 'utf8');
|
|
745
|
+
return statePath;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
async function installLinuxApp(options = {}) {
|
|
749
|
+
const installation = detectLinuxCodexDesktop(options);
|
|
750
|
+
if (!installation) {
|
|
751
|
+
const error = new Error(t('missingLinuxCodexDesktop', options.env || process.env));
|
|
752
|
+
error.code = 'CODEXPP_MISSING_LINUX_CODEX_DESKTOP';
|
|
753
|
+
throw error;
|
|
754
|
+
}
|
|
755
|
+
const installed = await installSidecars(options);
|
|
756
|
+
const shim = writeLinuxCodexShim(installation, options);
|
|
757
|
+
installed.linuxShim = shim.shimPath;
|
|
758
|
+
installed.linuxShimRoot = shim.shimRoot;
|
|
759
|
+
installed.linuxCodexStart = installation.startScript;
|
|
760
|
+
const entrypoints = await installEntrypoints(installed, { ...options, installRoot: installed.installRoot });
|
|
761
|
+
const statePath = writeLinuxInstallState(installed, installation, options);
|
|
762
|
+
return { installed, entrypoints, linux: installation, statePath };
|
|
763
|
+
}
|
|
764
|
+
|
|
444
765
|
function installSidecarRoot(options = {}) {
|
|
445
766
|
const platform = optionValue(options, 'platform', process.platform);
|
|
446
767
|
const fsImpl = options.fs || fs;
|
|
@@ -606,10 +927,17 @@ async function installEntrypoints(installed, options = {}) {
|
|
|
606
927
|
if (platform === 'darwin') {
|
|
607
928
|
return installMacEntrypoints(installed, options);
|
|
608
929
|
}
|
|
930
|
+
if (platform === 'linux') {
|
|
931
|
+
return installLinuxEntrypoints(installed, options);
|
|
932
|
+
}
|
|
609
933
|
return { unsupported: true };
|
|
610
934
|
}
|
|
611
935
|
|
|
612
936
|
async function installApp(options = {}) {
|
|
937
|
+
const platform = optionValue(options, 'platform', process.platform);
|
|
938
|
+
if (platform === 'linux') {
|
|
939
|
+
return installLinuxApp(options);
|
|
940
|
+
}
|
|
613
941
|
const installed = await installSidecars(options);
|
|
614
942
|
const entrypoints = await installEntrypoints(installed, { ...options, installRoot: installed.installRoot });
|
|
615
943
|
return { installed, entrypoints };
|
|
@@ -641,6 +969,15 @@ function inspectEntrypoints(options = {}) {
|
|
|
641
969
|
manager_path: fsImpl.existsSync(managerPrimary) ? managerPrimary : managerFallback,
|
|
642
970
|
};
|
|
643
971
|
}
|
|
972
|
+
if (platform === 'linux') {
|
|
973
|
+
const desktopPath = linuxEntrypointPath(options);
|
|
974
|
+
return {
|
|
975
|
+
silent: fsImpl.existsSync(desktopPath) ? 'installed' : 'missing',
|
|
976
|
+
manager: 'unsupported',
|
|
977
|
+
silent_path: desktopPath,
|
|
978
|
+
manager_path: '',
|
|
979
|
+
};
|
|
980
|
+
}
|
|
644
981
|
return { silent: 'unsupported', manager: 'unsupported' };
|
|
645
982
|
}
|
|
646
983
|
|
|
@@ -648,11 +985,12 @@ function installedSidecars(options = {}) {
|
|
|
648
985
|
const fsImpl = options.fs || fs;
|
|
649
986
|
const silent = installedSidecarPath('silent', options);
|
|
650
987
|
const manager = installedSidecarPath('manager', options);
|
|
988
|
+
const managerSupported = sidecarKindSupported('manager', options);
|
|
651
989
|
return {
|
|
652
990
|
silent,
|
|
653
991
|
manager,
|
|
654
992
|
silent_state: fsImpl.existsSync(silent) ? 'installed' : 'missing',
|
|
655
|
-
manager_state: fsImpl.existsSync(manager) ? 'installed' : 'missing',
|
|
993
|
+
manager_state: managerSupported ? (fsImpl.existsSync(manager) ? 'installed' : 'missing') : 'unsupported',
|
|
656
994
|
};
|
|
657
995
|
}
|
|
658
996
|
|
|
@@ -663,7 +1001,7 @@ function doctorReport(options = {}) {
|
|
|
663
1001
|
const sidecars = installedSidecars(options);
|
|
664
1002
|
const entrypoints = inspectEntrypoints(options);
|
|
665
1003
|
const supported = SUPPORTED_PLATFORMS.has(platformKey(platform, arch));
|
|
666
|
-
|
|
1004
|
+
const report = {
|
|
667
1005
|
platform,
|
|
668
1006
|
arch,
|
|
669
1007
|
supported: supported ? 'yes' : 'no',
|
|
@@ -681,6 +1019,16 @@ function doctorReport(options = {}) {
|
|
|
681
1019
|
silent_entrypoint: entrypoints.silent_path || '',
|
|
682
1020
|
manager_entrypoint: entrypoints.manager_path || '',
|
|
683
1021
|
};
|
|
1022
|
+
if (platform === 'linux') {
|
|
1023
|
+
const linux = detectLinuxCodexDesktop(options);
|
|
1024
|
+
const state = readJsonFile(linuxInstallStatePath(options), options.fs || fs) || {};
|
|
1025
|
+
report.linux_codex_desktop_state = linux ? 'found' : 'missing';
|
|
1026
|
+
report.linux_codex_desktop_start = (linux && linux.startScript) || state.codex_desktop_linux_start || '';
|
|
1027
|
+
report.linux_codex_shim = state.codex_shim || path.join(linuxShimRoot(options), LINUX_SHIM_BINARY);
|
|
1028
|
+
report.linux_codex_shim_state = (options.fs || fs).existsSync(report.linux_codex_shim) ? 'installed' : 'missing';
|
|
1029
|
+
report.install_mode = state.mode || (linux ? 'codex_desktop_linux' : 'unsupported');
|
|
1030
|
+
}
|
|
1031
|
+
return report;
|
|
684
1032
|
}
|
|
685
1033
|
|
|
686
1034
|
function printDoctor(jsonMode, options = {}) {
|
|
@@ -698,6 +1046,16 @@ function printDoctor(jsonMode, options = {}) {
|
|
|
698
1046
|
function spawnSidecar(kind, args = [], options = {}) {
|
|
699
1047
|
assertSupportedPlatform(options);
|
|
700
1048
|
const platform = optionValue(options, 'platform', process.platform);
|
|
1049
|
+
if (platform === 'linux' && kind === 'manager') {
|
|
1050
|
+
return { status: 1, error: new Error(t('unsupportedLinuxManager', options.env || process.env)) };
|
|
1051
|
+
}
|
|
1052
|
+
if (platform === 'linux' && kind === 'silent') {
|
|
1053
|
+
try {
|
|
1054
|
+
args = linuxSilentLaunchArgs(args, options);
|
|
1055
|
+
} catch (error) {
|
|
1056
|
+
return { status: 1, error };
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
701
1059
|
const cwd = options.cwd || os.homedir();
|
|
702
1060
|
const installed = installedSidecarPath(kind, options);
|
|
703
1061
|
const bundled = bundledSidecarPath(kind, options);
|
|
@@ -785,12 +1143,26 @@ async function runLauncher(args = [], options = {}) {
|
|
|
785
1143
|
}
|
|
786
1144
|
return { status: 1, error: new Error(message) };
|
|
787
1145
|
}
|
|
788
|
-
|
|
1146
|
+
let result;
|
|
1147
|
+
try {
|
|
1148
|
+
result = await installApp(options);
|
|
1149
|
+
} catch (error) {
|
|
1150
|
+
if (command === 'npm-postinstall' && error && error.code === 'CODEXPP_MISSING_LINUX_CODEX_DESKTOP') {
|
|
1151
|
+
console.log(`install_mode=unsupported`);
|
|
1152
|
+
console.log(`message=${error.message || String(error)}`);
|
|
1153
|
+
return { status: 0 };
|
|
1154
|
+
}
|
|
1155
|
+
throw error;
|
|
1156
|
+
}
|
|
789
1157
|
console.log(`install_mode=sidecar`);
|
|
790
1158
|
console.log(`upstream_version=${bundledUpstreamVersion(options) || 'missing'}`);
|
|
791
1159
|
console.log(`install_root=${result.installed.installRoot}`);
|
|
792
1160
|
console.log(`silent_binary=${result.installed.silent}`);
|
|
793
|
-
console.log(`manager_binary=${result.installed.manager}`);
|
|
1161
|
+
console.log(`manager_binary=${result.installed.manager || 'unsupported'}`);
|
|
1162
|
+
if (result.installed.linuxShim) {
|
|
1163
|
+
console.log(`linux_codex_shim=${result.installed.linuxShim}`);
|
|
1164
|
+
console.log(`linux_codex_desktop_start=${result.installed.linuxCodexStart}`);
|
|
1165
|
+
}
|
|
794
1166
|
console.log(`entrypoints=${result.entrypoints.skipped ? 'skipped' : 'installed'}`);
|
|
795
1167
|
return { status: 0 };
|
|
796
1168
|
}
|
package/npm/verify-package.cjs
CHANGED
|
@@ -7,6 +7,7 @@ const REQUIRED_PLATFORMS = {
|
|
|
7
7
|
'win32-x64': ['codex-plus-plus.exe', 'codex-plus-plus-manager.exe'],
|
|
8
8
|
'darwin-x64': ['codex-plus-plus', 'codex-plus-plus-manager'],
|
|
9
9
|
'darwin-arm64': ['codex-plus-plus', 'codex-plus-plus-manager'],
|
|
10
|
+
'linux-x64': ['codex-plus-plus'],
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
const REQUIRED_ICONS = {
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "v1.1.5",
|
|
3
|
-
"html_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/tag/v1.1.5",
|
|
4
|
-
"asset_name": "CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
5
|
-
"asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
6
|
-
"windows_asset_name": "CodexPlusPlus-1.1.5-windows-x64-setup.exe",
|
|
7
|
-
"windows_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-windows-x64-setup.exe",
|
|
8
|
-
"macos_x64_asset_name": "CodexPlusPlus-1.1.5-macos-x64.dmg",
|
|
9
|
-
"macos_x64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-x64.dmg",
|
|
10
|
-
"macos_arm64_asset_name": "CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
11
|
-
"macos_arm64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
12
|
-
"source_zip_url": "https://github.com/BigPizzaV3/CodexPlusPlus/archive/refs/tags/v1.1.5.zip",
|
|
13
|
-
"install_spec": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
14
|
-
"commit": "f6b69a3692e2833e29909c14164d4f6d309363b3",
|
|
15
|
-
"repository": "BigPizzaV3/CodexPlusPlus"
|
|
16
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"version": "v1.1.5",
|
|
3
|
+
"html_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/tag/v1.1.5",
|
|
4
|
+
"asset_name": "CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
5
|
+
"asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
6
|
+
"windows_asset_name": "CodexPlusPlus-1.1.5-windows-x64-setup.exe",
|
|
7
|
+
"windows_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-windows-x64-setup.exe",
|
|
8
|
+
"macos_x64_asset_name": "CodexPlusPlus-1.1.5-macos-x64.dmg",
|
|
9
|
+
"macos_x64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-x64.dmg",
|
|
10
|
+
"macos_arm64_asset_name": "CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
11
|
+
"macos_arm64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
12
|
+
"source_zip_url": "https://github.com/BigPizzaV3/CodexPlusPlus/archive/refs/tags/v1.1.5.zip",
|
|
13
|
+
"install_spec": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.5/CodexPlusPlus-1.1.5-macos-arm64.dmg",
|
|
14
|
+
"commit": "f6b69a3692e2833e29909c14164d4f6d309363b3",
|
|
15
|
+
"repository": "BigPizzaV3/CodexPlusPlus"
|
|
16
|
+
}
|
|
Binary file
|
|
Binary file
|