codex-ringside 0.0.0-stage → 0.1.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0-rc.0
4
+
5
+ First public candidate of Codex Ringside, formerly the local Codex Plus project.
6
+
7
+ - Usage allowance rings with reset-time tooltips.
8
+ - Images, desktop wallpaper, native glass fallback, cropping and color controls.
9
+ - Host appearance-mode integration when compatible functions are available.
10
+ - Explicit macOS agent installation and guarded startup handoff.
11
+ - Transactional Codex Plus migration, local backups and rollback on failure.
12
+ - CLI help/version commands; no automatic installation hooks.
13
+ - English/Chinese documentation, macOS CI and tag-driven trusted publishing.
14
+
15
+ Known limits: CLI/panel labels are Chinese; host internals can change; glass and dynamic wallpapers depend on host/macOS behavior. Read the README before installation.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 codex-ringside contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,29 @@
1
+ Codex Ringside is an unofficial add-on and is not affiliated with or endorsed by OpenAI.
2
+
3
+ Parts of this project are adapted from Codex Usage Card
4
+ (https://github.com/Lorie7813/codex-usage-card), v0.9.4 (release tag v0.9.4-beta.1), used under the MIT License:
5
+
6
+ The rate-limit merging, the app-server and CDP clients, the renderer card
7
+ structure and the startup handoff state machine.
8
+
9
+ MIT License
10
+
11
+ Copyright (c) 2026 Codex Usage Badge contributors
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,135 @@
1
- # Temporary Holding Version
1
+ # Codex Ringside
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ English · [简体中文](README.zh-CN.md)
4
+
5
+ Usage rings and customizable backgrounds for the Codex desktop sidebar on macOS.
6
+
7
+ See your remaining allowance at a glance, hover for reset times, and click a ring to configure a wallpaper, image or glass background. Image controls include cropping, positioning, zoom, blur and color adjustments.
8
+
9
+ **Unofficial community add-on. Not affiliated with or endorsed by OpenAI.** Parts are adapted from [Codex Usage Card](https://github.com/Lorie7813/codex-usage-card); see [NOTICE](NOTICE).
10
+
11
+ ![Usage ring and background settings](https://raw.githubusercontent.com/tccpc/codex-ringside/main/docs/images/background-settings.png)
12
+
13
+ ## Install
14
+
15
+ Requirements: macOS, Node.js 22+, Xcode Command Line Tools (`xcode-select --install`), and a signed-in compatible desktop client. The development host is `/Applications/ChatGPT.app`, version `26.1027.30824`, on Apple Silicon. Other host versions depend on compatible internal interfaces and are not individually verified. The CLI and settings panel currently use Chinese labels.
16
+
17
+ ```bash
18
+ npx codex-ringside@latest install
19
+ ```
20
+
21
+ If rings do not appear, save your work, quit the desktop app with ⌘Q, and reopen its original icon. The add-on may reopen a freshly launched client once, subject to the safeguards below.
22
+
23
+ Observe mode installs the agent without allowing it to restart the desktop client:
24
+
25
+ ```bash
26
+ npx codex-ringside@latest install --observe
27
+ ```
28
+
29
+ You can also use `npm install -g codex-ringside`, then `codex-ringside install`. Downloading the npm package alone never installs or starts the agent: there are no install lifecycle hooks. No `sudo` is needed with npx.
30
+
31
+ | Location | Contents |
32
+ | --- | --- |
33
+ | `~/Library/Application Support/CodexRingside` | Runtime, locally compiled bridge, settings and state |
34
+ | `~/Library/LaunchAgents/com.codexringside.agent.plist` | Background agent |
35
+ | `~/Library/Logs/CodexRingside` | Agent logs |
36
+ | `~/Library/Application Support/CodexRingside Backups` | Local installation/migration backups |
37
+
38
+ The installed runtime is independent of the checkout. For a custom application path:
39
+
40
+ ```bash
41
+ CODEX_RINGSIDE_APP="/Applications/ChatGPT.app" npx codex-ringside@latest install
42
+ ```
43
+
44
+ Resolution order: `CODEX_RINGSIDE_APP`, legacy `CODEX_PLUS_APP`, saved path, then `/Applications/ChatGPT.app`.
45
+
46
+ ## Upgrade and migrate from Codex Plus
47
+
48
+ Run `npx codex-ringside@latest install` again. Existing Ringside settings take precedence. First migration preserves the old application path, observe mode, background settings and images, then retires `com.codexplus.agent`.
49
+
50
+ The helper is compiled in a staging directory before services are stopped. Files are backed up before replacement; if the new watcher fails to become ready, the installer restores previous files and service state. A lock prevents overlapping install/uninstall operations. Installation itself does not restart an existing desktop session.
51
+
52
+ Backups remain in `CodexRingside Backups/install-*`. `receipt.json` records original paths and service states; `0` contains the previous Ringside installation and `1` the previous Codex Plus installation. Backups can contain private paths and images. Keep them local and remove them manually when satisfied with the upgrade. Uninstall preserves backups.
53
+
54
+ Reinstalling preserves observe mode; `install --observe` explicitly disables startup handoff. To re-enable it, unload the agent, set `allowClientRestart` to `true` in the installed `settings.json`, then run `install` again.
55
+
56
+ ## Background controls
57
+
58
+ Click a ring to open settings. Panel changes apply immediately; CLI changes apply within five seconds.
59
+
60
+ | Source | Behavior |
61
+ | --- | --- |
62
+ | `default` | Original appearance |
63
+ | `image` | Chosen image, positioned within the window |
64
+ | `wallpaper` | Desktop wallpaper aligned with the screen |
65
+ | `glass` | Host system glass, with a blurred-wallpaper fallback |
66
+
67
+ Transparency and blur control the effect. Images support cover, contain, stretch, center and tile layouts; drag positioning, scroll-to-zoom, crop and color adjustments. A new image resets position, crop and color while keeping transparency and blur. If transparency would hide a newly chosen image, it returns to 45% unless explicitly overridden.
68
+
69
+ The appearance switch uses the host's own system/light/dark setting and synchronizes with its settings page. It is hidden when compatible internal functions cannot be found.
70
+
71
+ ```bash
72
+ npx codex-ringside@latest background image ~/Pictures/photo.jpg --transparency 50 --blur 8
73
+ npx codex-ringside@latest background --layout contain --zoom 120 --y 20
74
+ npx codex-ringside@latest background --crop 10,0,80,60
75
+ npx codex-ringside@latest background default
76
+ ```
77
+
78
+ Options without a source keep the current source. `background` without arguments prints the saved configuration. Crop values are left, top, width and height in percentages; `--crop none` clears the crop. `--help` lists flags and `--version` prints the version; both work without an installed host.
79
+
80
+ ### Limitations
81
+
82
+ - Native glass depends on the host window state. In the tested host, unfocused, opaque or very large windows lose native glass; the glass source then uses blurred wallpaper. Ringside cannot change native window properties to produce clear transparency.
83
+ - Dynamic/aerial wallpapers may expose a different static image through macOS. Use a chosen image when an exact match matters.
84
+ - Wallpaper changes can take 60 seconds to refresh; alignment can briefly lag during window movement.
85
+ - Background cleanup uses host DOM heuristics. Terminals, code blocks, canvases and embedded web content are left alone; an unfamiliar page may retain opaque areas.
86
+ - Host updates can break rate-limit reading, placement or theme integration. Disable the background or uninstall if problems arise.
87
+
88
+ ## Status and troubleshooting
89
+
90
+ ```bash
91
+ npx codex-ringside@latest status
92
+ ```
93
+
94
+ Look for `agent: "running"`, `cdp.windows[].card.placed`, and `cdp.windows[].background.applied`. `nativeGlass: false` means the host disables native glass. `startup.event` explains skipped handoffs. Status may contain private paths and window URLs; redact it before sharing.
95
+
96
+ If no ring appears, verify the signed-in host, application path, Node version, Command Line Tools and whether port 39222 is occupied. Installing while the host is open does not trigger a restart. Save your work and reopen its original icon when convenient. Check `~/Library/Logs/CodexRingside` for errors.
97
+
98
+ Missing or expired allowance data is shown as unavailable. Account switches reset the app-server connection. The number of rings follows the valid allowance windows returned for your account.
99
+
100
+ ## Startup safeguards
101
+
102
+ Only a newly launched, single instance is considered: it must be in the foreground, at most eight seconds old, and have received no keyboard, click or scroll input since launch. Its command line must be known with no debug port, port 39222 must be free, and the previous attempt must be over 120 seconds old.
103
+
104
+ An app already running when the agent starts is never handed off. Only normal system `terminate()` is used: refusal or a ten-second exit timeout ends the attempt. User activity during handoff prevents focus stealing. Normal startup can take a few extra seconds and show the window reopening once.
105
+
106
+ ## Uninstall
107
+
108
+ ```bash
109
+ npx codex-ringside@latest uninstall
110
+ ```
111
+
112
+ This removes current and legacy agents, moves runtime/settings/logs to the Trash and removes injected UI. Backups remain. The desktop application bundle is unchanged. The debug port remains active until that desktop process exits; the next ordinary launch has no Ringside debug flag.
113
+
114
+ ## Security and privacy
115
+
116
+ Ringside starts the host's bundled `codex app-server`, reads `account/rateLimits/read` over stdio and refreshes every 60 seconds while a host window is connected. It injects UI through local Chrome DevTools Protocol port 39222 without modifying the signed app bundle.
117
+
118
+ **The debug endpoint has no authentication.** Local software can execute JavaScript in the desktop renderer through it. Never expose, forward or proxy port 39222. Use this add-on only where you trust the other local software.
119
+
120
+ Ringside does not read project files, chat content or the conversation database, and has no telemetry. It checks only the modification time of `auth.json`. The host CLI makes its own network requests. Wallpaper and chosen image processing is local; images/configuration are not uploaded. See [SECURITY.md](SECURITY.md).
121
+
122
+ ## Development
123
+
124
+ ```bash
125
+ git clone https://github.com/tccpc/codex-ringside.git
126
+ cd codex-ringside
127
+ npm ci --ignore-scripts
128
+ npm run verify
129
+ ```
130
+
131
+ Verification runs syntax checks, unit tests and tests the actual packed CLI and Swift source. There are no runtime npm dependencies. CI covers Node.js 22/24/26 and compiles on Apple Silicon and Intel macOS. Full host compatibility requires a real signed-in client.
132
+
133
+ For the browser-only fixture, run `python3 -m http.server 4173 --bind 127.0.0.1` from the checkout and open `http://127.0.0.1:4173/test/fixtures/card.html`. It uses simulated data and gradient images.
134
+
135
+ See [CONTRIBUTING.md](CONTRIBUTING.md), [release guide](docs/RELEASING.md) and [changelog](CHANGELOG.md). Distributed under [MIT](LICENSE), with upstream notices in [NOTICE](NOTICE).
@@ -0,0 +1,166 @@
1
+ # Codex Ringside
2
+
3
+ [English](README.md) · 简体中文
4
+
5
+ ![背景设置](https://raw.githubusercontent.com/tccpc/codex-ringside/main/docs/images/background-settings.png)
6
+
7
+ 在 Codex 桌面端的侧栏显示剩余额度圆环,悬停可看重置时间;点击圆环可以自定义 Codex 的背景:图片、壁纸或毛玻璃,透明度和模糊自由调节,图片还能裁剪、缩放和调整布局。仅支持 macOS。
8
+
9
+ 这是非官方插件,与 OpenAI 无关。原理参考 [Codex Usage Card](https://github.com/Lorie7813/codex-usage-card),署名见 [NOTICE](NOTICE)。
10
+
11
+ ## 工作原理
12
+
13
+ - **额度**:启动 Codex 内置的 `codex app-server`,通过 stdio 调用 `account/rateLimits/read`,每 60 秒刷新一次。
14
+ - **显示**:通过本机调试端口 `127.0.0.1:39222` 把卡片注入到 `nav[data-app-navigation-rail]`。
15
+ - **背景**:同样通过调试端口,把 Codex 外壳层的背景清空,在页面最底层放一张图片。窗口属性由主进程控制,插件改不了,所以“透明”透出的是 Codex 自带的系统毛玻璃,壁纸来源显示的是对齐到屏幕位置的桌面壁纸。
16
+ - **启动**:Codex 的 `app.asar` 受完整性校验保护,不能修改,只能让它带调试端口启动。后台任务发现你从原图标打开 Codex 时,会在安全条件都满足的情况下有序退出,再带端口重开一次。
17
+
18
+ 账号有几个额度窗口就显示几个圆环。圆环是分段刻度样式,环内显示剩余百分比和窗口名称(“5小时”或“周”)。额度充足时为青绿渐变,50% 及以下转为黄橙,低于 10% 转为橙红。
19
+
20
+ ## 安装
21
+
22
+ 需要 macOS、Node.js 22+ 和 Xcode Command Line Tools(`xcode-select --install`),以及已登录的兼容桌面客户端。开发与真机验证使用 `/Applications/ChatGPT.app`,版本 `26.1027.30824`。其他客户端版本依赖相同内部结构,未逐一验证。
23
+
24
+ 以下命令显式安装后台任务;仅下载 npm 包不会安装或启动后台。命令行和设置面板目前使用中文。
25
+
26
+ ```bash
27
+ npx codex-ringside@latest install
28
+ ```
29
+
30
+ 然后保存工作,用 ⌘Q 退出 Codex,再点原图标打开。
31
+
32
+ 只想看后台会做什么、不重启 Codex:
33
+
34
+ ```bash
35
+ npx codex-ringside@latest install --observe
36
+ ```
37
+
38
+ 安装内容:
39
+
40
+ | 位置 | 内容 |
41
+ |---|---|
42
+ | `~/Library/Application Support/CodexRingside` | 程序、`bridge`、设置和状态 |
43
+ | `~/Library/LaunchAgents/com.codexringside.agent.plist` | 后台任务 |
44
+ | `~/Library/Logs/CodexRingside` | 日志 |
45
+
46
+ 运行时不依赖这个仓库,改代码后要重新 `install`。
47
+
48
+ ## 升级和从 Codex Plus 迁移
49
+
50
+ 升级时重新运行 `npx codex-ringside@latest install`。已有新版设置优先;首次迁移保留旧版应用路径、观察模式、背景设置和图片,并停用 `com.codexplus.agent`。先暂存编译、备份,再切换后台;新后台未就绪时恢复原文件和运行状态。
51
+
52
+ 备份保留在 `~/Library/Application Support/CodexRingside Backups/install-*`。`receipt.json` 记录原路径及后台运行状态,`0` 是原 Ringside 安装,`1` 是原 Codex Plus 安装。卸载不会删除备份,确认新版正常后可手动清理。
53
+
54
+ 迁移和重装保留观察模式;`install --observe` 可明确关闭自动接管。要重新开启,退出后台后将安装目录 `settings.json` 的 `allowClientRestart` 改为 `true`,再运行 `install`。备份包含本地路径及图片,不要上传。
55
+
56
+ 非默认应用路径:
57
+
58
+ ```bash
59
+ CODEX_RINGSIDE_APP="/Applications/ChatGPT.app" npx codex-ringside@latest install
60
+ ```
61
+
62
+ 新环境变量优先于旧变量 `CODEX_PLUS_APP`,然后使用保存的应用路径,最后使用默认 `/Applications/ChatGPT.app`。
63
+
64
+ ## 自定义背景
65
+
66
+ 点击侧栏的圆环打开设置面板。先选来源,再用效果调外观,所有调节都立即生效。
67
+
68
+ 面板顶部还能切换 Codex 的**外观模式**:跟随系统、浅色、深色。它和设置页的“模式”是同一个设置,改动会保存,并在两边同步。这里用的是 Codex 内部的设置函数,找不到时控件自动隐藏。
69
+
70
+ **来源**决定背景是什么:
71
+
72
+ | 来源 | 背景 |
73
+ |---|---|
74
+ | `default` 关闭 | Codex 原样 |
75
+ | `image` 图片 | 你选的图片,按窗口摆放 |
76
+ | `wallpaper` 壁纸 | 桌面壁纸,按窗口在屏幕上的位置对齐 |
77
+ | `glass` 毛玻璃 | 系统毛玻璃,实时透出桌面和其他窗口 |
78
+
79
+ **效果**用透明度和模糊两个滑块自由调节,毛玻璃来源本身就是模糊的,只有透明度。
80
+
81
+ **图片编辑**只对图片来源生效:
82
+
83
+ - 拖动预览调位置,滚轮缩放,也可以用缩放、水平、垂直滑块
84
+ - 布局:铺满、适应、拉伸、居中、平铺
85
+ - 裁剪:拖动方框选区域,“匹配窗口”会框出和窗口同比例的最大区域
86
+ - 调色:亮度、对比度、饱和度
87
+
88
+ 换一张图片时,位置、缩放、裁剪和调色会重置,透明度和模糊保留。如果透明度已经高到看不见图片(95% 以上),选图片或切到图片、壁纸来源时会回到默认的 45%。
89
+
90
+ 也可以用命令行设置,5 秒内生效:
91
+
92
+ ```bash
93
+ npx codex-ringside@latest background image ~/Pictures/photo.jpg --transparency 50 --blur 8
94
+ ```
95
+
96
+ ```bash
97
+ npx codex-ringside@latest background --layout contain --zoom 120 --y 20
98
+ ```
99
+
100
+ ```bash
101
+ npx codex-ringside@latest background --crop 10,0,80,60
102
+ ```
103
+
104
+ ```bash
105
+ npx codex-ringside@latest background default
106
+ ```
107
+
108
+ 只写选项时保留当前来源,不带参数时打印当前设置。`--crop` 的四个数是左、上、宽、高的百分比,`--crop none` 取消裁剪。完整选项见不带参数运行 `npx codex-ringside@latest` 的用法提示。
109
+
110
+ 使用限制:
111
+
112
+ - **看不到其他窗口的情况**:窗口属性由主进程控制,不改 Codex 就做不到清晰的真透明。提高透明度后,窗口聚焦时透出的是带模糊的系统毛玻璃,失焦时透出的是纯色底。
113
+ - **毛玻璃有条件**:Codex 只在窗口聚焦、且窗口小于 3840×2160 物理像素时开启毛玻璃,Retina 屏上约等于 1920×1080pt,或者在设置里开了不透明窗口时也不会有。条件不满足时,毛玻璃来源自动换成模糊后的壁纸,不会跳成纯色。
114
+ - **壁纸可能不准**:动态壁纸和航拍壁纸只能拿到系统提供的静态图,和屏幕上的画面可能不同,这时改用图片来源。
115
+ - **切换有延迟**:切换桌面空间后,壁纸最多 60 秒才更新;拖动窗口时壁纸有轻微跟随延迟。
116
+ - **页面自动适配**:内容区里方角、无边框、占满区域的不透明层会被当作页面背景清除,设置页这类圆角卡片变成半透明磨砂,文字依然清楚。终端、代码块和内嵌网页不处理。设计不同的页面可能漏掉,这时自带背景的地方保持原样。
117
+
118
+ ## 查看状态
119
+
120
+ ```bash
121
+ npx codex-ringside@latest status
122
+ ```
123
+
124
+ 重点看 `cdp.windows[].card.placed`。背景看 `background`(保存的设置)和 `cdp.windows[].background`:`applied` 表示已生效,`nativeGlass` 为 `false` 表示 Codex 此刻关闭了毛玻璃。输出可能包含本机路径,分享前请先脱敏。
125
+
126
+ ## 卸载
127
+
128
+ ```bash
129
+ npx codex-ringside@latest uninstall
130
+ ```
131
+
132
+ 新旧后台任务会被移除,程序、设置和日志进废纸篓,安装备份保留。当前窗口的注入界面会移除;调试端口会保持到客户端退出,下次正常启动后关闭。桌面应用文件未改动。
133
+
134
+ ## 什么时候会重启 Codex
135
+
136
+ 下面的条件全部满足才会重启,否则只记录原因,不做任何动作:
137
+
138
+ - 这是新进程,且只有一个实例,后台任务启动前就在运行的窗口永远不动
139
+ - 启动不超过 8 秒,在前台,启动后没有按键、点击或滚动
140
+ - 进程参数里没有调试端口,且端口空闲
141
+ - 距上次尝试超过 120 秒
142
+
143
+ 退出只用系统的 `terminate()`,从不强杀。Codex 拒绝退出或 10 秒内没退出就放弃。
144
+
145
+ 所以这些情况下本次不会出现圆环:打开后马上开始操作、登录时自动启动、Codex 已经开着时才安装、两分钟内连续重开。下次从原图标打开即可。原因记录在 `status` 的 `startup.event` 里。
146
+
147
+ ## 风险
148
+
149
+ - **调试端口没有认证**:端口只绑定本机,但本机任何进程都能通过它在 Codex 窗口里执行 JS。不要转发或代理 39222 端口,也不要在装了不可信软件的机器上使用。
150
+ - **依赖内部接口**:依赖的 app-server 行为、侧栏 DOM 和外壳结构可能随客户端升级变化,导致额度或背景功能失效。发现异常时关闭背景或卸载插件。
151
+ - **启动变慢**:每次从原图标打开会多等几秒,窗口会重开一次。
152
+
153
+ ## 隐私
154
+
155
+ 只读取额度数据,不读项目文件、对话内容和 Codex 数据库,也没有任何上报。`auth.json` 只看修改时间,用来发现账号切换,不读内容。app-server 自身的网络请求由 Codex CLI 发出。背景功能只读取系统壁纸和你选的图片,转换后存在本机的 `CodexRingside/background` 目录,不会上传。
156
+
157
+ ## 开发
158
+
159
+ ```bash
160
+ npm ci --ignore-scripts
161
+ npm run verify
162
+ ```
163
+
164
+ 卡片样式可以用 `test/fixtures/card.html` 调试,不需要真实的 Codex。
165
+
166
+ 完整开发和发布流程见 [贡献指南](CONTRIBUTING.md)、[发布指南](docs/RELEASING.md);漏洞请通过 [私密安全报告](https://github.com/tccpc/codex-ringside/security/advisories/new) 提交。
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const { execFileSync } = require('node:child_process');
6
+ const { BackgroundStore, parseBackgroundArgs } = require('../src/background.cjs');
7
+ const { CDP_PORT, CdpSession, isMainWindow, listTargets } = require('../src/cdp.cjs');
8
+ const launchd = require('../src/launchd.cjs');
9
+ const installer = require('../src/install.cjs');
10
+ const { version } = require('../package.json');
11
+
12
+ const { installDir } = launchd.paths;
13
+ const settingsFile = path.join(installDir, 'settings.json');
14
+ const stateFile = path.join(installDir, 'state.json');
15
+
16
+ function readJson(file) {
17
+ try {
18
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
19
+ } catch {
20
+ return null;
21
+ }
22
+ }
23
+
24
+ async function install({ observe }) {
25
+ const result = await installer.install({ observe });
26
+ console.log('安装完成,后台任务已启动。');
27
+ console.log(result.settings.allowClientRestart
28
+ ? '如果圆环还没出现:保存工作后用 ⌘Q 退出 Codex,再点原图标打开。'
29
+ : '当前为观察模式:不会重启 Codex。');
30
+ console.log(`原安装备份:${result.backup}`);
31
+ }
32
+
33
+ // Reachable only if Codex is running with the port; without it there is no card to remove.
34
+ async function removeCard() {
35
+ try {
36
+ const targets = (await listTargets(CDP_PORT, 1000)).filter(isMainWindow);
37
+ await Promise.all(targets.map(async target => {
38
+ const session = new CdpSession(target);
39
+ await session.connect();
40
+ await session.evaluate('window.__codexRingsideCard?.destroy(); window.__codexRingsideBackground?.destroy(); window.__codexPlusCard?.destroy(); window.__codexPlusBackground?.destroy()');
41
+ session.close();
42
+ }));
43
+ } catch {
44
+ // Nothing is listening, so nothing is injected.
45
+ }
46
+ }
47
+
48
+ async function uninstall() {
49
+ const trash = await installer.uninstall({ removeUi: removeCard });
50
+ console.log(trash ? `已卸载,文件已移到废纸篓:${path.basename(trash)}` : '没有发现已安装的文件。');
51
+ console.log('Codex 本身未改动。下次从原图标打开时恢复原来的启动方式。');
52
+ }
53
+
54
+ async function cardStatus() {
55
+ try {
56
+ const targets = await listTargets(CDP_PORT, 1000);
57
+ const main = targets.filter(isMainWindow);
58
+ const windows = await Promise.all(main.map(async target => {
59
+ const session = new CdpSession(target);
60
+ await session.connect();
61
+ const { result } = await session.evaluate(
62
+ '({ card: window.__codexRingsideCard?.status() ?? null, background: window.__codexRingsideBackground?.status() ?? null })'
63
+ );
64
+ session.close();
65
+ return { url: target.url, ...result.value };
66
+ }));
67
+ return { port: 'open', targets: targets.length, windows };
68
+ } catch {
69
+ return { port: 'closed', targets: 0, windows: [] };
70
+ }
71
+ }
72
+
73
+ function listeners() {
74
+ try {
75
+ return execFileSync('/usr/sbin/lsof', ['-nP', `-iTCP:${CDP_PORT}`, '-sTCP:LISTEN'], { encoding: 'utf8' })
76
+ .trim().split('\n').slice(1)
77
+ .map(line => line.trim().split(/\s+/).slice(-2, -1)[0]);
78
+ } catch {
79
+ return [];
80
+ }
81
+ }
82
+
83
+ async function status() {
84
+ const job = launchd.printJob();
85
+ const state = readJson(stateFile);
86
+ console.log(JSON.stringify({
87
+ agent: job ? job.match(/state = (\S+)/)?.[1] ?? 'loaded' : 'not installed',
88
+ settings: readJson(settingsFile),
89
+ background: new BackgroundStore({ root: installDir }).config,
90
+ startup: state && { event: state.event, updatedAt: new Date(state.updatedAt).toISOString() },
91
+ listenAddresses: listeners(),
92
+ cdp: await cardStatus()
93
+ }, null, 2));
94
+ }
95
+
96
+ // Only writes the file: the agent applies it on its next scan, the same way as a panel change.
97
+ async function background(args) {
98
+ if (!fs.existsSync(settingsFile)) throw new Error('尚未安装,请先运行 install');
99
+ const store = new BackgroundStore({ root: installDir });
100
+ if (args.length === 0) {
101
+ console.log(JSON.stringify(store.config, null, 2));
102
+ return;
103
+ }
104
+ const config = await store.commit(parseBackgroundArgs(args));
105
+ console.log(`背景来源:${config.source},5 秒内生效。`);
106
+ }
107
+
108
+ async function main() {
109
+ const [command, ...args] = process.argv.slice(2);
110
+ if (command === '--version' || command === '-v') { console.log(version); return; }
111
+ if (command === 'install') {
112
+ const unknown = args.filter(arg => arg !== '--observe');
113
+ if (unknown.length) throw new Error(`未知参数:${unknown.join(' ')}`);
114
+ await install({ observe: args.includes('--observe') ? true : undefined });
115
+ }
116
+ else if (command === 'uninstall') await uninstall();
117
+ else if (command === 'status') await status();
118
+ else if (command === 'background') await background(args);
119
+ else {
120
+ console.log('用法:codex-ringside <install [--observe] | uninstall | status | background [来源] [选项]>');
121
+ console.log('背景来源:default | image <图片路径> | wallpaper | glass');
122
+ console.log('背景效果:--transparency <0-100> --blur <0-60>');
123
+ console.log('图片调整:--layout <cover|contain|stretch|center|tile> --zoom <10-400> --x <0-100> --y <0-100>');
124
+ console.log(' --crop <左,上,宽,高|none> --brightness <0-200> --contrast <0-200> --saturation <0-200>');
125
+ process.exitCode = !command || ['--help', '-h', 'help'].includes(command) ? 0 : 1;
126
+ }
127
+ }
128
+
129
+ main().catch(error => {
130
+ console.error(`错误:${error.message}`);
131
+ process.exitCode = 1;
132
+ });