[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80835":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":15,"stars7d":16,"stars30d":17,"stars90d":14,"forks30d":14,"starsTrendScore":16,"compositeScore":18,"rankGlobal":9,"rankLanguage":9,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":22,"hasPages":20,"topics":23,"createdAt":9,"pushedAt":9,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":14,"starSnapshotCount":14,"syncStatus":12,"lastSyncTime":27,"discoverSource":28},80835,"codex-lamp","loopbrew\u002Fcodex-lamp","loopbrew","Codex lamp",null,"Python",40,2,36,0,1,3,4,1.43,"MIT License",false,"main",true,[],"2026-06-12 02:04:07","# Codex Lamp\n\nA cool physical status light for Codex.\n\nThis project connects Codex lifecycle hooks to a [Moonside](https:\u002F\u002Fmoonside.design\u002F) lamp, so the\nlamp changes color or animation as Codex starts working, asks for approval, or\nfinishes a turn.\n\nIt is intentionally small:\n\n- Codex hooks stay fast and quiet.\n- A shell hook writes the desired lamp state.\n- One background Python daemon owns the Bluetooth connection.\n- The daemon sends simple Moonside BLE commands.\n\n```text\nCodex hook event\n  -> codex_lamp_hook.sh\n       writes \u002Ftmp\u002Fcodex_lamp_state\n       starts the daemon if needed\n  -> codex_lamp_daemon.py\n       keeps one BLE connection alive\n       watches for state changes\n       sends commands to the lamp\n```\n\n## What It Looks Like\n\n| Codex moment | Lamp state | Default lamp effect |\n| --- | --- | --- |\n| Codex starts, resumes, or clears a session | `idle` | Warm amber solid light |\n| You submit a prompt | `working` | Animated blue\u002Fwhite working theme |\n| Codex runs a supported tool | `working` | Animated blue\u002Fwhite working theme |\n| Codex asks for approval | `input` | Purple solid light |\n| Codex finishes a turn | `idle` | Warm amber solid light |\n| Manual off or idle timeout | `off` | LED off |\n\nThe default effects are defined in `codex_lamp_daemon.py`:\n\n```python\nCOLOR_IDLE = \"COLOR255180050\"\nWORKING_CMD = \"THEME.BEAT2.255,255,255,0,0,140,\"\nCOLOR_INPUT = \"THEME.WAVE1.255,100,0,255,26,214,\"\n```\n\nYou can change these commands to any Moonside command your lamp accepts. Moonside API Documentation: https:\u002F\u002Fdeveloper.moonside.design\u002F\n\n## Project Files\n\n| File | Purpose |\n| --- | --- |\n| `hooks.json` | Example Codex hook configuration. Copy or merge this into `~\u002F.codex\u002Fhooks.json`. |\n| `codex_lamp_hook.sh` | Fast Codex hook entrypoint. It writes the state file and starts the daemon. |\n| `codex_lamp_daemon.py` | Background BLE daemon. It watches the state file and controls the lamp. |\n| `codex_lamp_test.py` | Manual BLE tester for scanning, colors, themes, and raw commands. |\n| `README.md` | Setup, usage, and troubleshooting guide. |\n\n## Requirements\n\n- macOS\n- Python 3.10 or newer\n- A Moonside lamp that accepts Nordic UART Service commands\n- Bluetooth enabled\n- The Python package `bleak`\n- Codex with local hooks support\n\nInstall `bleak` into the Python environment that Codex can reach:\n\n```sh\npython3 -m pip install bleak\n```\n\nThe hook auto-detects Python in this order:\n\n1. `CODEX_LAMP_PYTHON`, if set\n2. `python3`\n3. `\u002Fopt\u002Fhomebrew\u002Fbin\u002Fpython3`\n4. `$CONDA_PREFIX\u002Fbin\u002Fpython3`, if Conda is active\n\n## Quick Start\n\n### 1. Test the lamp directly\n\nFrom this project folder:\n\n```sh\npython3 codex_lamp_test.py scan\n```\n\nYou should see something like:\n\n```text\nMOONSIDE-O101    A2F26067-F4DB-DAD8-FB91-70D6A2E9CCC0\n```\n\nThen test a few direct commands:\n\n```sh\npython3 codex_lamp_test.py off\npython3 codex_lamp_test.py color 255 180 50\npython3 codex_lamp_test.py color 255 0 0\npython3 codex_lamp_test.py theme BEAT2 --colors 255,255,255,0,0,140\n```\n\nIf these work, Bluetooth, Python, `bleak`, and the lamp are all talking.\n\n### 2. Test the hook and daemon locally\n\nStill from this project folder:\n\n```sh\nbash codex_lamp_hook.sh working\nbash codex_lamp_hook.sh input\nbash codex_lamp_hook.sh idle\nbash codex_lamp_hook.sh off\n```\n\nThe lamp should react to each state.\n\nCheck the daemon log if something does not respond:\n\n```sh\ntail -n 80 \u002Ftmp\u002Fcodex_lamp_daemon.log\n```\n\n### 3. Install the scripts for Codex\n\nCopy the scripts into a stable location under your Codex home:\n\n```sh\nmkdir -p ~\u002F.codex\u002Fcodex-lamp\ncp codex_lamp_hook.sh codex_lamp_daemon.py codex_lamp_test.py ~\u002F.codex\u002Fcodex-lamp\u002F\nchmod +x ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh\nchmod +x ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_daemon.py\nchmod +x ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_test.py\n```\n\nCopy or merge the example hook config:\n\n```sh\ncp hooks.json ~\u002F.codex\u002Fhooks.json\n```\n\nIf you already have `~\u002F.codex\u002Fhooks.json`, merge the `hooks` entries instead of\noverwriting your existing file.\n\n### 4. Make sure Codex hooks are enabled\n\nCurrent Codex builds enable hooks by default. To be explicit, you can add this\nto `~\u002F.codex\u002Fconfig.toml`:\n\n```toml\n[features]\nhooks = true\n```\n\nOlder Codex builds used the deprecated alias:\n\n```toml\n[features]\ncodex_hooks = true\n```\n\nPrefer `hooks = true` for current Codex versions.\n\nAfter changing hook configuration, restart Codex.\n\n### 5. Review and trust the hook\n\nCodex may ask you to review non-managed command hooks before they run. In the\nCodex CLI, use:\n\n```text\n\u002Fhooks\n```\n\nReview the command paths and trust the hooks if they match your local install.\n\n## Hook Mapping\n\nThe included `hooks.json` maps Codex events to lamp states:\n\n| Codex hook | When it fires | Lamp state |\n| --- | --- | --- |\n| `SessionStart` | Codex starts, resumes, or clears a session | `idle` |\n| `UserPromptSubmit` | You send a prompt to Codex | `working` |\n| `PreToolUse` | Codex is about to run a supported tool | `working` |\n| `PostToolUse` | Codex finished a supported tool call | `working` |\n| `PermissionRequest` | Codex is about to ask for approval | `input` |\n| `Stop` | Codex finished the turn | `idle` |\n\nThe `PermissionRequest` hook is intentionally narrow. It only fires when Codex\nis about to ask for approval, such as a sandbox escalation or managed network\napproval. It does not fire every time Codex is waiting for your next message.\n\n## Manual Commands\n\nYou can drive the lamp without Codex:\n\n```sh\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh working\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh input\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh idle\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh off\n```\n\nThese are useful for testing, filming demos, or resetting the lamp.\n\n## Demoing PermissionRequest\n\n`PermissionRequest` is not common during normal use because Codex only asks for\napproval when it crosses a permission boundary.\n\nFor a predictable demo, start Codex in a stricter mode and ask it to do a\nharmless write:\n\n```sh\ncodex --sandbox read-only --ask-for-approval on-request\n```\n\nThen prompt Codex:\n\n```text\nCreate a harmless file named permission_demo.txt in this folder with the text\n\"Codex lamp demo\". Since this session is read-only, request approval before\nmaking the edit.\n```\n\nExpected lamp flow:\n\n```text\nprompt submitted -> working\napproval needed  -> input\napproved action  -> working\nturn complete    -> idle\n```\n\n## Configuration\n\nThe scripts can be configured with environment variables.\n\n| Variable | Default | Purpose |\n| --- | --- | --- |\n| `CODEX_LAMP_PYTHON` | auto-detect | Python executable that has `bleak` installed. |\n| `CODEX_LAMP_DAEMON` | script next to the hook | Override daemon script path. |\n| `CODEX_LAMP_NAME_PREFIX` | `MOONSIDE` | BLE device name prefix to scan for. |\n| `CODEX_LAMP_ADDRESS` | unset | Pin one lamp by BLE address or macOS UUID. |\n| `CODEX_LAMP_IDLE_TIMEOUT` | `1800` | Seconds before idle daemon turns the lamp off and exits. |\n| `CODEX_LAMP_STATE_FILE` | `\u002Ftmp\u002Fcodex_lamp_state` | Shared desired-state file. |\n| `CODEX_LAMP_PID_FILE` | `\u002Ftmp\u002Fcodex_lamp_daemon.pid` | Daemon PID file. |\n| `CODEX_LAMP_LOCK_FILE` | `\u002Ftmp\u002Fcodex_lamp_daemon.lock` | Lock file that prevents duplicate daemons. |\n| `CODEX_LAMP_LOG_FILE` | `\u002Ftmp\u002Fcodex_lamp_daemon.log` | Hook and daemon log file. |\n\n### Pin a specific lamp\n\nIf you have multiple BLE devices nearby, pin the lamp address:\n\n```sh\nexport CODEX_LAMP_ADDRESS=\"A2F26067-F4DB-DAD8-FB91-70D6A2E9CCC0\"\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh idle\n```\n\nFor permanent use, set the environment variable wherever your Codex shell\nenvironment is configured.\n\n### Shorten the idle auto-off time\n\nBy default, the daemon turns the lamp off after 30 minutes of idle time. For a\nshorter timeout:\n\n```sh\nexport CODEX_LAMP_IDLE_TIMEOUT=120\n```\n\nThat means:\n\n```text\nCodex finishes -> idle light\n2 minutes pass -> lamp off\n```\n\n## Customizing Effects\n\nEdit these constants in `codex_lamp_daemon.py`:\n\n```python\nWORKING_CMD = \"THEME.BEAT2.255,255,255,0,0,140,\"\nCOLOR_IDLE = \"COLOR255180050\"\nCOLOR_INPUT = \"COLOR200000255\"\n```\n\nFor example, to make approval requests use an animated wave instead of a solid\npurple color:\n\n```python\nCOLOR_INPUT = \"THEME.WAVE1.255,100,0,255,26,214,\"\n```\n\nAfter changing the installed daemon file, restart the daemon:\n\n```sh\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh off\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh idle\n```\n\n## Moonside Commands Used\n\nThis project sends ASCII commands over the Nordic UART Service write\ncharacteristic:\n\n```text\n6e400002-b5a3-f393-e0a9-e50e24dcca9e\n```\n\nExamples:\n\n| Action | Command |\n| --- | --- |\n| LED on | `LEDON` |\n| LED off | `LEDOFF` |\n| Solid color | `COLOR255180050` |\n| Brightness | `BRIGH060` |\n| Theme | `THEME.BEAT2.255,255,255,0,0,140,` |\n\nUse the tester to try raw commands:\n\n```sh\npython3 codex_lamp_test.py raw \"LEDOFF\"\npython3 codex_lamp_test.py raw \"THEME.WAVE1.255,100,0,255,26,214,\"\n```\n\n## Multiple Sessions\n\nThis version is a single-lamp, shared-state design.\n\nAll Codex sessions write to the same state file:\n\n```text\n\u002Ftmp\u002Fcodex_lamp_state\n```\n\nThat means the latest hook event wins.\n\nExample:\n\n```text\nSession A -> working\nlamp      -> working\n\nSession B -> idle\nlamp      -> idle\n```\n\nEven if Session A is still working, Session B's later `idle` event can overwrite\nthe lamp state.\n\nFor one main Codex session, this is simple and works well. For heavy multi-session\nuse, the next design would parse Codex's hook JSON from `stdin`, track each\n`session_id`, and compute a global state:\n\n```text\nif any session needs approval -> input\nelse if any session is working -> working\nelse -> idle\n```\n\nFor multiple lamps, a future router could assign each active `session_id` to a\nspecific lamp.\n\n## Session Close Behavior\n\nCodex currently exposes `Stop` as a turn-level hook, not a true\nsession\u002Fwindow-close hook.\n\nSo this project can reliably detect:\n\n```text\nturn finished -> idle\n```\n\nIt cannot detect perfectly:\n\n```text\nterminal window closed\ndesktop app window closed\nsession abandoned\n```\n\nThe practical workaround is the idle timeout. If Codex stops producing hook\nevents, the daemon eventually turns the lamp off.\n\n## Troubleshooting\n\n### The lamp does not show up\n\nRun:\n\n```sh\npython3 codex_lamp_test.py scan --all\n```\n\nIf you see the lamp with a different name, use:\n\n```sh\npython3 codex_lamp_test.py --name-prefix YOUR_PREFIX scan\n```\n\nIf scanning is flaky, pin the address:\n\n```sh\nCODEX_LAMP_ADDRESS=\"YOUR-LAMP-ADDRESS\" python3 codex_lamp_test.py off\n```\n\n### `bleak` is installed, but the hook cannot find it\n\nFind the Python that has `bleak`:\n\n```sh\npython3 -c \"import sys, bleak; print(sys.executable)\"\n```\n\nThen point the hook at it:\n\n```sh\nexport CODEX_LAMP_PYTHON=\"\u002Fpath\u002Fto\u002Fpython3\"\n```\n\n### The direct tester works, but Codex does not trigger the lamp\n\nCheck these in order:\n\n1. `~\u002F.codex\u002Fhooks.json` contains the hook commands.\n2. The hook paths point to the installed script.\n3. The script is executable.\n4. Codex has reviewed\u002Ftrusted the hook with `\u002Fhooks`.\n5. Hooks are not disabled in `~\u002F.codex\u002Fconfig.toml`.\n6. The daemon log has useful clues:\n\n```sh\ntail -n 120 \u002Ftmp\u002Fcodex_lamp_daemon.log\n```\n\nYou can also manually run the exact installed command:\n\n```sh\nbash \"$HOME\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh\" working\n```\n\n### The daemon seems stuck\n\nTurn the lamp off through the hook first:\n\n```sh\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh off\n```\n\nIf needed, stop the daemon manually:\n\n```sh\nkill \"$(cat \u002Ftmp\u002Fcodex_lamp_daemon.pid)\"\nrm -f \u002Ftmp\u002Fcodex_lamp_daemon.pid \u002Ftmp\u002Fcodex_lamp_daemon.lock\n```\n\nThen start it again:\n\n```sh\nbash ~\u002F.codex\u002Fcodex-lamp\u002Fcodex_lamp_hook.sh idle\n```\n\n### The Desktop app behaves differently from the CLI\n\nStart by testing with the Codex CLI, because hook behavior is easiest to inspect\nthere with `\u002Fhooks` and the local logs.\n\nIf your Desktop build does not appear to trigger hooks, keep the hook project as\nis and consider a separate Desktop watcher that observes Codex logs or app\nactivity and calls the same `codex_lamp_hook.sh` states. That keeps the BLE code\ncentralized and avoids duplicating lamp control logic.\n\n## Safety Notes\n\n- These scripts control your own Bluetooth lamp at your own risk.\n- Codex hooks run local commands, so read the scripts before trusting them.\n- The hook is intentionally quiet and exits `0` so lamp failures do not break\n  Codex turns.\n- The daemon writes temporary state, PID, lock, and log files under `\u002Ftmp` by\n  default.\n\n## Credits\n\nThis project was inspired by [bobek-balinek\u002Fclaude-lamp](https:\u002F\u002Fgithub.com\u002Fbobek-balinek\u002Fclaude-lamp), a Claude hook project for controlling a Moonside lamp from Claude activity.\n\n## Useful Links\n\n- Codex hooks documentation: https:\u002F\u002Fdevelopers.openai.com\u002Fcodex\u002Fhooks\n- Codex configuration basics: https:\u002F\u002Fdevelopers.openai.com\u002Fcodex\u002Fconfig-basic\n- Bleak documentation: https:\u002F\u002Fbleak.readthedocs.io\u002F\n","Codex Lamp 是一个用于 Codex 的物理状态灯项目。它通过蓝牙将 Codex 生命周期事件与 Moonside 灯连接起来，根据 Codex 的不同工作状态（如开始、等待用户输入、完成等）改变灯的颜色或动画效果。该项目设计简洁高效：Codex 钩子保持快速响应，通过一个简单的 shell 脚本记录所需灯的状态，而 Python 守护进程则负责维护蓝牙连接并发送指令到灯。适用于需要直观反馈 Codex 运行状态的场景，例如开发调试或演示环境。","2026-06-11 04:02:31","CREATED_QUERY"]