@duanluan/codex-plus-plus-launcher 0.1.13 → 1.2.1

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 CHANGED
@@ -23,7 +23,7 @@ npm install -g @duanluan/codex-plus-plus-launcher
23
23
 
24
24
  postinstall 只安装入口和快捷方式,不会自动启动 Codex++、管理工具或 Codex 进程。
25
25
 
26
- Windows/npm 路线默认把“更新 Codex++”收敛成“更新这个 wrapper 包”。也就是说,用户获取最新 Codex++ 能力的主要方式是升级 `@duanluan/codex-plus-plus-launcher`;wrapper 在发版时会重新同步并打包当时最新的上游 GitHub Release,而不是要求用户单独运行上游 `.exe` / `.dmg` 安装器。
26
+ Windows/npm 路线默认把“更新 Codex++”变成“更新这个 wrapper 包”。也就是说,用户获取最新 Codex++ 能力的主要方式是升级 `@duanluan/codex-plus-plus-launcher`;wrapper 在发版时会重新同步并打包当时最新的上游 GitHub Release,而不是要求用户单独运行上游 `.exe` / `.dmg` 安装器。
27
27
 
28
28
  ## 常用命令
29
29
 
@@ -1 +1 @@
1
- __version__ = "0.1.13"
1
+ __version__ = "1.2.1"
@@ -11,7 +11,7 @@ import tempfile
11
11
  import time
12
12
  from dataclasses import dataclass
13
13
  from datetime import datetime, timezone
14
- from pathlib import Path
14
+ from pathlib import Path, PureWindowsPath
15
15
  from typing import Any
16
16
 
17
17
  from codex_plus_plus_launcher import __version__
@@ -741,7 +741,7 @@ def install_windows_shortcuts(paths: RuntimePaths, mode: str = "real") -> Instal
741
741
  start_menu_dir = Path(os.environ.get("APPDATA", str(Path.home() / "AppData" / "Roaming"))) / "Microsoft" / "Windows" / "Start Menu" / "Programs"
742
742
  desktop_link = desktop_dir / "Codex++.lnk"
743
743
  start_menu_link = start_menu_dir / "Codex++.lnk"
744
- shortcut_target = Path(os.environ.get("SystemRoot", r"C:\Windows")) / "System32" / "wscript.exe"
744
+ shortcut_target = Path(str(PureWindowsPath(os.environ.get("SystemRoot", r"C:\Windows")) / "System32" / "wscript.exe"))
745
745
  arguments = f'"{launcher_script}"'
746
746
  working_directory = paths.home
747
747
  description = "Launch Codex++"
@@ -9,6 +9,14 @@ function packageRoot() {
9
9
  return path.resolve(__dirname, '..');
10
10
  }
11
11
 
12
+ function packageVersion(root = packageRoot()) {
13
+ return JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
14
+ }
15
+
16
+ function defaultUpstreamRef(root = packageRoot()) {
17
+ return `v${packageVersion(root)}`;
18
+ }
19
+
12
20
  function run(command, args, options = {}) {
13
21
  const result = spawnSync(command, args, { stdio: 'inherit', shell: false, ...options });
14
22
  if (result.status !== 0) {
@@ -16,6 +24,11 @@ function run(command, args, options = {}) {
16
24
  }
17
25
  }
18
26
 
27
+ function runSucceeds(command, args, options = {}) {
28
+ const result = spawnSync(command, args, { stdio: 'ignore', shell: false, ...options });
29
+ return result.status === 0;
30
+ }
31
+
19
32
  function platformKey(platform = process.platform, arch = process.arch) {
20
33
  return `${platform}-${arch}`;
21
34
  }
@@ -42,10 +55,20 @@ function packageBuildArgs(key) {
42
55
  return key === 'linux-x64' ? ['-p', 'codex-plus-launcher'] : [];
43
56
  }
44
57
 
58
+ function applyPluginUnlockPatch(upstreamDir, root = packageRoot()) {
59
+ const patchPath = path.join(root, 'patches', 'codex-plus-plus-plugin-unlock.patch');
60
+ if (runSucceeds('git', ['apply', '--reverse', '--check', patchPath], { cwd: upstreamDir })) {
61
+ return;
62
+ }
63
+ run('git', ['apply', '--check', patchPath], { cwd: upstreamDir });
64
+ run('git', ['apply', patchPath], { cwd: upstreamDir });
65
+ }
66
+
45
67
  function main() {
46
68
  const root = packageRoot();
47
- const upstreamDir = process.env.CODEXPP_UPSTREAM_DIR || path.join(os.tmpdir(), 'CodexPlusPlus-upstream-build');
48
- const ref = process.env.CODEXPP_UPSTREAM_REF || 'v1.1.7';
69
+ const ref = process.env.CODEXPP_UPSTREAM_REF || defaultUpstreamRef(root);
70
+ const refKey = ref.replace(/[^A-Za-z0-9._-]/g, '_');
71
+ const upstreamDir = process.env.CODEXPP_UPSTREAM_DIR || path.join(os.tmpdir(), `CodexPlusPlus-upstream-build-${refKey}`);
49
72
  const key = process.env.CODEXPP_SIDECAR_PLATFORM || platformKey();
50
73
  const outDir = path.join(root, 'upstream-bin', key);
51
74
 
@@ -56,6 +79,8 @@ function main() {
56
79
  run('git', ['checkout', 'FETCH_HEAD'], { cwd: upstreamDir });
57
80
  }
58
81
 
82
+ applyPluginUnlockPatch(upstreamDir, root);
83
+
59
84
  if (key !== 'linux-x64') {
60
85
  run('npm', ['install', '--package-lock=false'], { cwd: path.join(upstreamDir, 'apps', 'codex-plus-manager'), shell: process.platform === 'win32' });
61
86
  run('npm', ['run', 'vite:build'], { cwd: path.join(upstreamDir, 'apps', 'codex-plus-manager'), shell: process.platform === 'win32' });
@@ -100,4 +125,4 @@ if (require.main === module) {
100
125
  }
101
126
  }
102
127
 
103
- module.exports = { buildTargetArgs, cargoTargetForKey, main, packageBuildArgs, platformKey };
128
+ module.exports = { applyPluginUnlockPatch, buildTargetArgs, cargoTargetForKey, defaultUpstreamRef, main, packageBuildArgs, platformKey };
package/npm/launcher.js CHANGED
@@ -13,6 +13,8 @@ const MANAGER_NAME = 'Codex++ 管理工具';
13
13
  const LINUX_SHIM_DIR_NAME = 'codex-desktop-linux-shim';
14
14
  const LINUX_SHIM_BINARY = 'codex.exe';
15
15
  const LINUX_DESKTOP_ENTRY = 'codex-plus-plus.desktop';
16
+ const PLUGIN_AUTH_UNLOCK_FILE = 'plugin-auth-unlocked.js';
17
+ const PLUGIN_AUTH_UNLOCK_CONTENT = 'function e(e){return false}export{e as t};\n';
16
18
 
17
19
  function optionValue(options, key, fallback) {
18
20
  const value = options[key];
@@ -63,6 +65,11 @@ function upstreamMetadataPath(options = {}) {
63
65
  return path.join(root, 'upstream-bin', 'upstream-release.json');
64
66
  }
65
67
 
68
+ function pluginAuthUnlockPath(options = {}) {
69
+ const root = optionValue(options, 'packageRoot', packageRoot);
70
+ return optionValue(options, 'pluginAuthUnlockPath', () => path.join(root, 'npm', PLUGIN_AUTH_UNLOCK_FILE));
71
+ }
72
+
66
73
  function readJsonFile(candidate, fsImpl = fs) {
67
74
  try {
68
75
  return JSON.parse(fsImpl.readFileSync(candidate, 'utf8'));
@@ -585,11 +592,113 @@ function shellSingleQuote(value) {
585
592
  return `'${String(value).replace(/'/g, "'\\''")}'`;
586
593
  }
587
594
 
588
- function linuxCodexShimScript(startScript) {
595
+ function linuxCodexShimScript(startScript, pluginAuthUnlockFile) {
589
596
  return [
590
597
  '#!/usr/bin/env bash',
591
598
  'set -euo pipefail',
592
- `exec ${shellSingleQuote(startScript)} -- "$@"`,
599
+ '',
600
+ `START_SCRIPT=${shellSingleQuote(startScript)}`,
601
+ `PLUGIN_AUTH_UNLOCK_FILE=${shellSingleQuote(pluginAuthUnlockFile)}`,
602
+ 'TMP_APP_ROOT=""',
603
+ 'CHILD_PID=""',
604
+ '',
605
+ 'resolve_file_dir() {',
606
+ ' local source="$1"',
607
+ ' local dir',
608
+ ' while [ -L "$source" ]; do',
609
+ ' dir="$(cd -P "$(dirname "$source")" && pwd)"',
610
+ ' source="$(readlink "$source")"',
611
+ ' case "$source" in',
612
+ ' /*) ;;',
613
+ ' *) source="$dir/$source" ;;',
614
+ ' esac',
615
+ ' done',
616
+ ' cd -P "$(dirname "$source")" && pwd',
617
+ '}',
618
+ '',
619
+ 'cleanup() {',
620
+ ' if [ -n "$TMP_APP_ROOT" ]; then',
621
+ ' rm -rf "$TMP_APP_ROOT"',
622
+ ' fi',
623
+ '}',
624
+ '',
625
+ 'forward_signal() {',
626
+ ' local sig="$1"',
627
+ ' if [ -n "$CHILD_PID" ] && kill -0 "$CHILD_PID" 2>/dev/null; then',
628
+ ' kill -"$sig" "$CHILD_PID" 2>/dev/null || true',
629
+ ' fi',
630
+ '}',
631
+ '',
632
+ 'trap cleanup EXIT',
633
+ "trap 'forward_signal HUP' HUP",
634
+ "trap 'forward_signal INT' INT",
635
+ "trap 'forward_signal TERM' TERM",
636
+ '',
637
+ 'APP_ROOT="$(resolve_file_dir "$START_SCRIPT")"',
638
+ 'WEBVIEW_DIR="$APP_ROOT/content/webview"',
639
+ '',
640
+ 'if [ ! -f "$PLUGIN_AUTH_UNLOCK_FILE" ]; then',
641
+ ' echo "Codex++ plugin auth unlock file not found: $PLUGIN_AUTH_UNLOCK_FILE" >&2',
642
+ ' exit 1',
643
+ 'fi',
644
+ '',
645
+ 'if [ ! -d "$WEBVIEW_DIR" ]; then',
646
+ ' exec "$START_SCRIPT" -- "$@"',
647
+ 'fi',
648
+ '',
649
+ 'TMP_APP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/codex-plus-plus-linux-app.XXXXXX")"',
650
+ 'cp "$START_SCRIPT" "$TMP_APP_ROOT/start.sh"',
651
+ 'chmod 755 "$TMP_APP_ROOT/start.sh"',
652
+ '',
653
+ 'shopt -s dotglob nullglob',
654
+ 'for entry in "$APP_ROOT"/*; do',
655
+ ' name="$(basename "$entry")"',
656
+ ' if [ "$name" = "start.sh" ] || [ "$name" = "content" ]; then',
657
+ ' continue',
658
+ ' fi',
659
+ ' ln -s "$entry" "$TMP_APP_ROOT/$name"',
660
+ 'done',
661
+ 'shopt -u dotglob nullglob',
662
+ '',
663
+ 'mkdir -p "$TMP_APP_ROOT/content/webview/assets"',
664
+ 'if [ -d "$APP_ROOT/content" ]; then',
665
+ ' shopt -s dotglob nullglob',
666
+ ' for entry in "$APP_ROOT/content"/*; do',
667
+ ' name="$(basename "$entry")"',
668
+ ' if [ "$name" = "webview" ]; then',
669
+ ' continue',
670
+ ' fi',
671
+ ' ln -s "$entry" "$TMP_APP_ROOT/content/$name"',
672
+ ' done',
673
+ ' shopt -u dotglob nullglob',
674
+ 'fi',
675
+ '',
676
+ 'shopt -s dotglob nullglob',
677
+ 'for entry in "$WEBVIEW_DIR"/*; do',
678
+ ' name="$(basename "$entry")"',
679
+ ' if [ "$name" = "assets" ]; then',
680
+ ' continue',
681
+ ' fi',
682
+ ' ln -s "$entry" "$TMP_APP_ROOT/content/webview/$name"',
683
+ 'done',
684
+ 'for entry in "$WEBVIEW_DIR/assets"/*; do',
685
+ ' name="$(basename "$entry")"',
686
+ ' if [[ "$name" == plugin-auth-*.js ]]; then',
687
+ ' ln -s "$PLUGIN_AUTH_UNLOCK_FILE" "$TMP_APP_ROOT/content/webview/assets/$name"',
688
+ ' else',
689
+ ' ln -s "$entry" "$TMP_APP_ROOT/content/webview/assets/$name"',
690
+ ' fi',
691
+ 'done',
692
+ 'shopt -u dotglob nullglob',
693
+ '',
694
+ '"$TMP_APP_ROOT/start.sh" -- "$@" &',
695
+ 'CHILD_PID=$!',
696
+ 'set +e',
697
+ 'wait "$CHILD_PID"',
698
+ 'STATUS=$?',
699
+ 'set -e',
700
+ 'CHILD_PID=""',
701
+ 'exit "$STATUS"',
593
702
  '',
594
703
  ].join('\n');
595
704
  }
@@ -599,9 +708,16 @@ function writeLinuxCodexShim(installation, options = {}) {
599
708
  const shimRoot = linuxShimRoot(options);
600
709
  fsImpl.mkdirSync(shimRoot, { recursive: true });
601
710
  const shimPath = path.join(shimRoot, LINUX_SHIM_BINARY);
602
- fsImpl.writeFileSync(shimPath, linuxCodexShimScript(installation.startScript), 'utf8');
711
+ const pluginAuthTarget = path.join(shimRoot, PLUGIN_AUTH_UNLOCK_FILE);
712
+ const pluginAuthSource = pluginAuthUnlockPath(options);
713
+ if (fsImpl.existsSync(pluginAuthSource)) {
714
+ fsImpl.copyFileSync(pluginAuthSource, pluginAuthTarget);
715
+ } else {
716
+ fsImpl.writeFileSync(pluginAuthTarget, PLUGIN_AUTH_UNLOCK_CONTENT, 'utf8');
717
+ }
718
+ fsImpl.writeFileSync(shimPath, linuxCodexShimScript(installation.startScript, pluginAuthTarget), 'utf8');
603
719
  fsImpl.chmodSync(shimPath, 0o755);
604
- return { shimRoot, shimPath };
720
+ return { shimRoot, shimPath, pluginAuthUnlock: pluginAuthTarget };
605
721
  }
606
722
 
607
723
  function linuxDesktopEntry({ name, execPath, iconPath, comment }) {
@@ -756,6 +872,7 @@ async function installLinuxApp(options = {}) {
756
872
  const shim = writeLinuxCodexShim(installation, options);
757
873
  installed.linuxShim = shim.shimPath;
758
874
  installed.linuxShimRoot = shim.shimRoot;
875
+ installed.linuxPluginAuthUnlock = shim.pluginAuthUnlock;
759
876
  installed.linuxCodexStart = installation.startScript;
760
877
  const entrypoints = await installEntrypoints(installed, { ...options, installRoot: installed.installRoot });
761
878
  const statePath = writeLinuxInstallState(installed, installation, options);
@@ -0,0 +1 @@
1
+ function e(e){return false}export{e as t};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@duanluan/codex-plus-plus-launcher",
3
- "version": "0.1.13",
3
+ "version": "1.2.1",
4
4
  "description": "Install and launch Codex++ from npm",
5
5
  "bin": {
6
6
  "cxpp": "npm/cxpp.js",
@@ -15,12 +15,14 @@
15
15
  "files": [
16
16
  "codex_plus_plus_launcher/*.py",
17
17
  "codex_plus_plus_launcher/assets/*",
18
+ "patches/**",
18
19
  "upstream-bin/**",
19
20
  "install-npm.cjs",
20
21
  "npm/cxpp.js",
21
22
  "npm/i18n.js",
22
23
  "npm/launcher.js",
23
24
  "npm/install.js",
25
+ "npm/plugin-auth-unlocked.js",
24
26
  "npm/build-local-binary.cjs",
25
27
  "npm/verify-package.cjs",
26
28
  "npm/smoke-install.cjs",
@@ -0,0 +1,164 @@
1
+ --- a/assets/inject/renderer-inject.js
2
+ +++ b/assets/inject/renderer-inject.js
3
+ @@ -67,0 +68 @@
4
+ + const codexPluginNavUnlockVersion = "2";
5
+ @@ -2701,0 +2703,154 @@
6
+ + function pluginNavButtonCandidates() {
7
+ + return Array.from(document.querySelectorAll(selectors.pluginNavButton))
8
+ + .filter((button) => button instanceof HTMLElement && isPluginNavButton(button));
9
+ + }
10
+ +
11
+ + function isPluginNavButton(button) {
12
+ + const text = (button.textContent || "").trim();
13
+ + if (/^(Plugins|插件)$/.test(text)) return true;
14
+ + return !!button.querySelector?.(selectors.pluginSvgPath);
15
+ + }
16
+ +
17
+ + function reactPropKeys(element) {
18
+ + return Object.keys(element || {}).filter((key) => key.startsWith("__reactProps"));
19
+ + }
20
+ +
21
+ + function reactPropsFor(element) {
22
+ + return reactPropKeys(element)
23
+ + .map((key) => element[key])
24
+ + .filter((props) => props && typeof props === "object");
25
+ + }
26
+ +
27
+ + function reactFiberFor(element) {
28
+ + const key = Object.keys(element || {}).find((key) => key.startsWith("__reactFiber"));
29
+ + return key ? element[key] : null;
30
+ + }
31
+ +
32
+ + function reactPropHandlersFor(element) {
33
+ + const handlers = reactPropsFor(element)
34
+ + .map((props) => props.onClick)
35
+ + .filter((handler) => typeof handler === "function");
36
+ + const fiber = reactFiberFor(element);
37
+ + [
38
+ + fiber?.memoizedProps?.onClick,
39
+ + fiber?.pendingProps?.onClick,
40
+ + fiber?.alternate?.memoizedProps?.onClick,
41
+ + fiber?.alternate?.pendingProps?.onClick,
42
+ + ].forEach((handler) => {
43
+ + if (typeof handler === "function") handlers.push(handler);
44
+ + });
45
+ + return Array.from(new Set(handlers));
46
+ + }
47
+ +
48
+ + function reactClickHandlerFor(element) {
49
+ + for (const handler of reactPropHandlersFor(element)) {
50
+ + if (typeof handler === "function") return handler;
51
+ + }
52
+ + return null;
53
+ + }
54
+ +
55
+ + function isUsefulPluginNavClickHandler(handler) {
56
+ + if (typeof handler !== "function") return false;
57
+ + const source = Function.prototype.toString.call(handler);
58
+ + if (/^\s*(?:\(\)\s*=>\s*\{\s*\}|function\s*[^(]*\([^)]*\)\s*\{\s*\})\s*$/.test(source)) return false;
59
+ + if (source.includes("navigateToCodexPlugins")) return false;
60
+ + return true;
61
+ + }
62
+ +
63
+ + function isSkillsRouteClickHandler(handler) {
64
+ + if (!isUsefulPluginNavClickHandler(handler)) return false;
65
+ + const source = Function.prototype.toString.call(handler);
66
+ + return source.includes("Uy(") || source.includes("/skills");
67
+ + }
68
+ +
69
+ + function isSkillsNavButton(button) {
70
+ + const text = (button.textContent || "").trim();
71
+ + return /^(Skills|技能)$/.test(text);
72
+ + }
73
+ +
74
+ + function pluginNavFallbackClickHandler(button) {
75
+ + const ownSkillsHandler = reactPropHandlersFor(button).find(isSkillsRouteClickHandler);
76
+ + if (ownSkillsHandler) return ownSkillsHandler;
77
+ + const currentHandler = reactClickHandlerFor(button);
78
+ + if (isUsefulPluginNavClickHandler(currentHandler)) return currentHandler;
79
+ + const nav = button.closest?.('nav[role="navigation"]');
80
+ + const candidates = Array.from(nav?.querySelectorAll?.(selectors.pluginNavButton) || []);
81
+ + for (const candidate of candidates) {
82
+ + if (candidate === button) continue;
83
+ + const handlers = reactPropHandlersFor(candidate);
84
+ + const skillsHandler = handlers.find(isSkillsRouteClickHandler);
85
+ + if (skillsHandler) return skillsHandler;
86
+ + }
87
+ + const skillsButton = candidates.find((candidate) => candidate !== button && isSkillsNavButton(candidate));
88
+ + const skillsHandler = reactClickHandlerFor(skillsButton);
89
+ + if (isUsefulPluginNavClickHandler(skillsHandler)) return skillsHandler;
90
+ + return null;
91
+ + }
92
+ +
93
+ + function patchReactPluginNavProps(element) {
94
+ + const pluginHandler = pluginNavFallbackClickHandler(element);
95
+ + reactPropsFor(element)
96
+ + .forEach((props) => {
97
+ + props.disabled = false;
98
+ + props["aria-disabled"] = false;
99
+ + props["data-disabled"] = undefined;
100
+ + if (pluginHandler && !isUsefulPluginNavClickHandler(props.onClick)) {
101
+ + props.onClick = pluginHandler;
102
+ + }
103
+ + });
104
+ + }
105
+ +
106
+ + function clearPluginNavDisabledState(element) {
107
+ + if (!(element instanceof HTMLElement)) return;
108
+ + if ("disabled" in element) element.disabled = false;
109
+ + element.removeAttribute("disabled");
110
+ + element.removeAttribute("aria-disabled");
111
+ + element.removeAttribute("data-disabled");
112
+ + element.removeAttribute("inert");
113
+ + element.classList.remove("disabled", "opacity-50", "cursor-not-allowed", "pointer-events-none");
114
+ + element.style.pointerEvents = "auto";
115
+ + element.style.opacity = "";
116
+ + element.style.cursor = "pointer";
117
+ + element.tabIndex = 0;
118
+ + patchReactPluginNavProps(element);
119
+ + }
120
+ +
121
+ + function pluginNavUnlockNodes(button) {
122
+ + const nodes = [button];
123
+ + button.querySelectorAll?.("[disabled], [aria-disabled], [data-disabled], .cursor-not-allowed, .pointer-events-none")
124
+ + .forEach((node) => nodes.push(node));
125
+ + let parent = button.parentElement;
126
+ + for (let depth = 0; parent && depth < 3; depth += 1, parent = parent.parentElement) {
127
+ + if (parent.matches?.("button, [role='button'], [disabled], [aria-disabled], [data-disabled], .cursor-not-allowed, .pointer-events-none")) {
128
+ + nodes.push(parent);
129
+ + }
130
+ + }
131
+ + return Array.from(new Set(nodes));
132
+ + }
133
+ +
134
+ + function unlockPluginNavButtons() {
135
+ + if (!codexPlusSettings().pluginMarketplaceUnlock) return;
136
+ + pluginNavButtonCandidates().forEach((button) => {
137
+ + button.dataset.codexPluginNavUnlocked = codexPluginNavUnlockVersion;
138
+ + pluginNavUnlockNodes(button).forEach(clearPluginNavDisabledState);
139
+ + });
140
+ + }
141
+ +
142
+ + function refreshPluginNavUnlockLoop() {
143
+ + const shouldRun = codexPlusSettings().pluginMarketplaceUnlock;
144
+ + if (!shouldRun) {
145
+ + clearInterval(window.__codexPluginNavUnlockRefreshTimer);
146
+ + window.__codexPluginNavUnlockRefreshTimer = null;
147
+ + return;
148
+ + }
149
+ + if (window.__codexPluginNavUnlockRefreshTimer) return;
150
+ + window.__codexPluginNavUnlockRefreshTimer = setInterval(() => {
151
+ + if (!codexPlusSettings().pluginMarketplaceUnlock) {
152
+ + clearInterval(window.__codexPluginNavUnlockRefreshTimer);
153
+ + window.__codexPluginNavUnlockRefreshTimer = null;
154
+ + return;
155
+ + }
156
+ + unlockPluginNavButtons();
157
+ + }, codexForcePluginInstallRefreshIntervalMs);
158
+ + }
159
+ +
160
+ @@ -7592,0 +7748,2 @@
161
+ + unlockPluginNavButtons();
162
+ + refreshPluginNavUnlockLoop();
163
+ @@ -7647,0 +7805 @@
164
+ + selectors.pluginNavButton,
@@ -1,16 +1,16 @@
1
1
  {
2
- "version": "v1.1.7",
3
- "html_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/tag/v1.1.7",
4
- "asset_name": "CodexPlusPlus-1.1.7-macos-arm64.dmg",
5
- "asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.7/CodexPlusPlus-1.1.7-macos-arm64.dmg",
6
- "windows_asset_name": "CodexPlusPlus-1.1.7-windows-x64-setup.exe",
7
- "windows_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.7/CodexPlusPlus-1.1.7-windows-x64-setup.exe",
8
- "macos_x64_asset_name": "CodexPlusPlus-1.1.7-macos-x64.dmg",
9
- "macos_x64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.7/CodexPlusPlus-1.1.7-macos-x64.dmg",
10
- "macos_arm64_asset_name": "CodexPlusPlus-1.1.7-macos-arm64.dmg",
11
- "macos_arm64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.7/CodexPlusPlus-1.1.7-macos-arm64.dmg",
12
- "source_zip_url": "https://github.com/BigPizzaV3/CodexPlusPlus/archive/refs/tags/v1.1.7.zip",
13
- "install_spec": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.1.7/CodexPlusPlus-1.1.7-macos-arm64.dmg",
14
- "commit": "333c2b0c0d0167fa181df1690140f14c863dad6f",
2
+ "version": "v1.2.1",
3
+ "html_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/tag/v1.2.1",
4
+ "asset_name": "CodexPlusPlus-1.2.1-macos-arm64.dmg",
5
+ "asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.2.1/CodexPlusPlus-1.2.1-macos-arm64.dmg",
6
+ "windows_asset_name": "CodexPlusPlus-1.2.1-windows-x64-setup.exe",
7
+ "windows_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.2.1/CodexPlusPlus-1.2.1-windows-x64-setup.exe",
8
+ "macos_x64_asset_name": "CodexPlusPlus-1.2.1-macos-x64.dmg",
9
+ "macos_x64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.2.1/CodexPlusPlus-1.2.1-macos-x64.dmg",
10
+ "macos_arm64_asset_name": "CodexPlusPlus-1.2.1-macos-arm64.dmg",
11
+ "macos_arm64_asset_url": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.2.1/CodexPlusPlus-1.2.1-macos-arm64.dmg",
12
+ "source_zip_url": "https://github.com/BigPizzaV3/CodexPlusPlus/archive/refs/tags/v1.2.1.zip",
13
+ "install_spec": "https://github.com/BigPizzaV3/CodexPlusPlus/releases/download/v1.2.1/CodexPlusPlus-1.2.1-macos-arm64.dmg",
14
+ "commit": "b20ca373d2e5a29bb75b532bd93f74bac18626f5",
15
15
  "repository": "BigPizzaV3/CodexPlusPlus"
16
16
  }