dhi-orbit 0.3.0__tar.gz

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.
Files changed (100) hide show
  1. dhi_orbit-0.3.0/LICENSE +21 -0
  2. dhi_orbit-0.3.0/PKG-INFO +217 -0
  3. dhi_orbit-0.3.0/README.md +201 -0
  4. dhi_orbit-0.3.0/chatdash/__init__.py +2 -0
  5. dhi_orbit-0.3.0/chatdash/cp/__init__.py +0 -0
  6. dhi_orbit-0.3.0/chatdash/cp/hooks/__init__.py +0 -0
  7. dhi_orbit-0.3.0/chatdash/cp/hooks/ask_hook.py +5 -0
  8. dhi_orbit-0.3.0/chatdash/cp/hooks/stop_hook.py +5 -0
  9. dhi_orbit-0.3.0/chatdash/statusline.py +7 -0
  10. dhi_orbit-0.3.0/dhi_orbit/__init__.py +2 -0
  11. dhi_orbit-0.3.0/dhi_orbit/__main__.py +4 -0
  12. dhi_orbit-0.3.0/dhi_orbit/accounts.py +350 -0
  13. dhi_orbit-0.3.0/dhi_orbit/actions.py +416 -0
  14. dhi_orbit-0.3.0/dhi_orbit/collector.py +344 -0
  15. dhi_orbit-0.3.0/dhi_orbit/config.py +237 -0
  16. dhi_orbit-0.3.0/dhi_orbit/cp/__init__.py +5 -0
  17. dhi_orbit-0.3.0/dhi_orbit/cp/api.py +810 -0
  18. dhi_orbit-0.3.0/dhi_orbit/cp/autolock.py +31 -0
  19. dhi_orbit-0.3.0/dhi_orbit/cp/corrections.py +125 -0
  20. dhi_orbit-0.3.0/dhi_orbit/cp/db.py +143 -0
  21. dhi_orbit-0.3.0/dhi_orbit/cp/decisions.py +239 -0
  22. dhi_orbit-0.3.0/dhi_orbit/cp/devserver.py +192 -0
  23. dhi_orbit-0.3.0/dhi_orbit/cp/drafts.py +164 -0
  24. dhi_orbit-0.3.0/dhi_orbit/cp/fileindex.py +201 -0
  25. dhi_orbit-0.3.0/dhi_orbit/cp/graphx.py +313 -0
  26. dhi_orbit-0.3.0/dhi_orbit/cp/hooks/__init__.py +0 -0
  27. dhi_orbit-0.3.0/dhi_orbit/cp/hooks/ask_hook.py +129 -0
  28. dhi_orbit-0.3.0/dhi_orbit/cp/hooks/stop_hook.py +115 -0
  29. dhi_orbit-0.3.0/dhi_orbit/cp/idlecompact.py +287 -0
  30. dhi_orbit-0.3.0/dhi_orbit/cp/intent.py +173 -0
  31. dhi_orbit-0.3.0/dhi_orbit/cp/limits.py +64 -0
  32. dhi_orbit-0.3.0/dhi_orbit/cp/mount.py +130 -0
  33. dhi_orbit-0.3.0/dhi_orbit/cp/notify.py +80 -0
  34. dhi_orbit-0.3.0/dhi_orbit/cp/receipts.py +243 -0
  35. dhi_orbit-0.3.0/dhi_orbit/cp/resume.py +182 -0
  36. dhi_orbit-0.3.0/dhi_orbit/cp/risk.py +42 -0
  37. dhi_orbit-0.3.0/dhi_orbit/cp/send.py +65 -0
  38. dhi_orbit-0.3.0/dhi_orbit/cp/shadow.py +131 -0
  39. dhi_orbit-0.3.0/dhi_orbit/cp/sources.py +296 -0
  40. dhi_orbit-0.3.0/dhi_orbit/cp/transcript.py +140 -0
  41. dhi_orbit-0.3.0/dhi_orbit/cp/work.py +526 -0
  42. dhi_orbit-0.3.0/dhi_orbit/cp/workitems.py +19 -0
  43. dhi_orbit-0.3.0/dhi_orbit/extract.py +236 -0
  44. dhi_orbit-0.3.0/dhi_orbit/index.py +61 -0
  45. dhi_orbit-0.3.0/dhi_orbit/keepwarm.py +376 -0
  46. dhi_orbit-0.3.0/dhi_orbit/panels.py +120 -0
  47. dhi_orbit-0.3.0/dhi_orbit/server.py +531 -0
  48. dhi_orbit-0.3.0/dhi_orbit/static/index.html +358 -0
  49. dhi_orbit-0.3.0/dhi_orbit/statusline.py +83 -0
  50. dhi_orbit-0.3.0/dhi_orbit/ui/css/app.css +698 -0
  51. dhi_orbit-0.3.0/dhi_orbit/ui/css/nebula.css +636 -0
  52. dhi_orbit-0.3.0/dhi_orbit/ui/icon.svg +6 -0
  53. dhi_orbit-0.3.0/dhi_orbit/ui/index.html +52 -0
  54. dhi_orbit-0.3.0/dhi_orbit/ui/js/accounts.js +114 -0
  55. dhi_orbit-0.3.0/dhi_orbit/ui/js/answer.js +80 -0
  56. dhi_orbit-0.3.0/dhi_orbit/ui/js/api.js +54 -0
  57. dhi_orbit-0.3.0/dhi_orbit/ui/js/app.js +435 -0
  58. dhi_orbit-0.3.0/dhi_orbit/ui/js/board.js +380 -0
  59. dhi_orbit-0.3.0/dhi_orbit/ui/js/capacity.js +154 -0
  60. dhi_orbit-0.3.0/dhi_orbit/ui/js/cards.js +221 -0
  61. dhi_orbit-0.3.0/dhi_orbit/ui/js/chat.js +218 -0
  62. dhi_orbit-0.3.0/dhi_orbit/ui/js/dictate.js +108 -0
  63. dhi_orbit-0.3.0/dhi_orbit/ui/js/field.js +473 -0
  64. dhi_orbit-0.3.0/dhi_orbit/ui/js/gl-nebula.js +463 -0
  65. dhi_orbit-0.3.0/dhi_orbit/ui/js/gl-worker.js +20 -0
  66. dhi_orbit-0.3.0/dhi_orbit/ui/js/graph.js +822 -0
  67. dhi_orbit-0.3.0/dhi_orbit/ui/js/gx.js +303 -0
  68. dhi_orbit-0.3.0/dhi_orbit/ui/js/lib.js +114 -0
  69. dhi_orbit-0.3.0/dhi_orbit/ui/js/md.js +100 -0
  70. dhi_orbit-0.3.0/dhi_orbit/ui/js/nebula.js +158 -0
  71. dhi_orbit-0.3.0/dhi_orbit/ui/js/palette.js +100 -0
  72. dhi_orbit-0.3.0/dhi_orbit/ui/js/settings.js +147 -0
  73. dhi_orbit-0.3.0/dhi_orbit/ui/js/sky.js +365 -0
  74. dhi_orbit-0.3.0/dhi_orbit/ui/js/spawn.js +72 -0
  75. dhi_orbit-0.3.0/dhi_orbit/ui/js/voice.js +185 -0
  76. dhi_orbit-0.3.0/dhi_orbit/ui/js/workitem.js +113 -0
  77. dhi_orbit-0.3.0/dhi_orbit/ui/manifest.webmanifest +10 -0
  78. dhi_orbit-0.3.0/dhi_orbit/ui/sw.js +17 -0
  79. dhi_orbit-0.3.0/dhi_orbit/usage_meter.py +185 -0
  80. dhi_orbit-0.3.0/dhi_orbit.egg-info/PKG-INFO +217 -0
  81. dhi_orbit-0.3.0/dhi_orbit.egg-info/SOURCES.txt +98 -0
  82. dhi_orbit-0.3.0/dhi_orbit.egg-info/dependency_links.txt +1 -0
  83. dhi_orbit-0.3.0/dhi_orbit.egg-info/entry_points.txt +10 -0
  84. dhi_orbit-0.3.0/dhi_orbit.egg-info/top_level.txt +2 -0
  85. dhi_orbit-0.3.0/pyproject.toml +45 -0
  86. dhi_orbit-0.3.0/setup.cfg +4 -0
  87. dhi_orbit-0.3.0/tests/test_accounts.py +115 -0
  88. dhi_orbit-0.3.0/tests/test_config.py +232 -0
  89. dhi_orbit-0.3.0/tests/test_cp.py +446 -0
  90. dhi_orbit-0.3.0/tests/test_cp_decisions.py +226 -0
  91. dhi_orbit-0.3.0/tests/test_cp_drafts_corrections.py +102 -0
  92. dhi_orbit-0.3.0/tests/test_cp_graphx.py +140 -0
  93. dhi_orbit-0.3.0/tests/test_cp_receipts.py +166 -0
  94. dhi_orbit-0.3.0/tests/test_cp_resume.py +319 -0
  95. dhi_orbit-0.3.0/tests/test_cp_shadow.py +62 -0
  96. dhi_orbit-0.3.0/tests/test_cp_work.py +199 -0
  97. dhi_orbit-0.3.0/tests/test_dhi_orbit.py +173 -0
  98. dhi_orbit-0.3.0/tests/test_keepwarm.py +353 -0
  99. dhi_orbit-0.3.0/tests/test_meter_script.py +47 -0
  100. dhi_orbit-0.3.0/tests/test_usage_meter.py +197 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dev Sanghvi
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.
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: dhi-orbit
3
+ Version: 0.3.0
4
+ Summary: A local dashboard for every Claude Code chat across your config dirs.
5
+ Author: Dev Sanghvi
6
+ License: MIT
7
+ Keywords: claude-code,dashboard,local
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: MacOS
11
+ Classifier: Topic :: Software Development
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # DHI Orbit
18
+
19
+ A local web dashboard that shows every Claude Code chat across one or more config directories on one page.
20
+
21
+ - A board of what needs you: questions, permission prompts, blocked jobs, and chats stalled on a usage limit.
22
+ - Reply to a chat from the page (one normal turn in that chat), stop it, or open it in Terminal.
23
+ - Usage limits per account (5-hour and 7-day), set up when you connect the account; never a guess.
24
+ - A graph of accounts, chats, work items and the files they touch.
25
+ - Optional WebGL "nebula" look (Settings, Look). The default look is plain and needs no GPU.
26
+ - Standard library only: no dependencies, no build step, no CDN.
27
+
28
+ Viewing costs zero model tokens: DHI Orbit only reads files. A reply you send is one normal turn in that chat.
29
+
30
+ ## Screenshots
31
+
32
+ Screenshots are not included yet (placeholder).
33
+
34
+ ## Install
35
+
36
+ Requires Python 3.10 or newer.
37
+
38
+ Homebrew (macOS or Linux):
39
+
40
+ ```sh
41
+ brew install Dv04/dhi-orbit/dhi-orbit
42
+ ```
43
+
44
+ pipx:
45
+
46
+ ```sh
47
+ pipx install git+https://github.com/Dv04/dhi-orbit
48
+ ```
49
+
50
+ pip, in any virtualenv:
51
+
52
+ ```sh
53
+ python3 -m pip install git+https://github.com/Dv04/dhi-orbit
54
+ ```
55
+
56
+ You also need Claude Code itself (`claude` on your `PATH`).
57
+
58
+ ## Quick start
59
+
60
+ ```sh
61
+ dhi-orbit
62
+ ```
63
+
64
+ `orbit` is a shorter name for the same command. Then open <http://127.0.0.1:8787/v2/>. `dhi-orbit --help` lists the options (`--port`, `--window-hours`, `--every`, `--no-notify`). macOS notifications for chats that need you are on by default; `--no-notify` turns them off.
65
+
66
+ On first run DHI Orbit creates its data directory, `~/.config/dhi-orbit` (or `$DHI_ORBIT_HOME`), with mode 700, and an
67
+ access token inside it (`.token`, mode 600). The page gets the token injected when it is served from loopback.
68
+
69
+ ### Upgrading from chatdash
70
+
71
+ DHI Orbit is the new name of chatdash. The `chatdash` command still works (as do `chatdash-ask-hook`,
72
+ `chatdash-stop-hook` and `chatdash-statusline`), an existing `~/.config/chatdash` data directory (with its
73
+ `chatdash.db`) keeps being used, and the `CHATDASH_*` environment variables are still read; the `DHI_ORBIT_*`
74
+ name wins when both are set. A status line meter or hook that chatdash installed keeps running (a small
75
+ `chatdash` module forwards to DHI Orbit) and the meter is rewritten to the new command the next time you turn
76
+ usage on for that account. While the old data directory is in use, the Schedules panel keeps managing your
77
+ `com.chatdash.*` launchd jobs; new installs use `com.dhi.orbit`. Python imports are now `dhi_orbit`.
78
+
79
+ ## Connect your accounts
80
+
81
+ Open **Settings > Accounts** (a fresh install opens on that screen). Type a name, for example `work`, and press
82
+ **Connect account**. DHI Orbit creates `~/.claude-work` and runs Claude Code's own `claude auth login` for it:
83
+ a claude.com sign-in page opens in this computer's browser, you approve, and the account shows as signed in.
84
+ From another device (the page on your phone, say), open the sign-in link shown there instead, approve, and paste
85
+ the code claude.com shows; it is typed into Claude Code's own prompt.
86
+
87
+ DHI Orbit never sees, stores or sends your password or tokens: Claude Code writes its credentials into that
88
+ account's folder exactly as when you sign in in a terminal. Accounts you already use (`~/.claude`, any
89
+ `~/.claude-<name>`) appear by themselves.
90
+
91
+ - **Disconnect** hides an account from the board; its sign-in and chats are untouched. **Reconnect** shows it again.
92
+ - **Delete** signs the account out (`claude auth logout`) and moves its folder to the Trash. It needs you to type
93
+ the account name, and refuses while a chat on it is running. `~/.claude` (your default) is only signed out and
94
+ hidden, never moved.
95
+
96
+ Use an account in a terminal with `CLAUDE_CONFIG_DIR=~/.claude-work claude`, or start chats from the board.
97
+ Only connect accounts that are yours; Anthropic's terms do not allow sharing logins.
98
+
99
+ ## Multi-account
100
+
101
+ DHI Orbit looks for config directories in your home folder: `~/.claude` and any `~/.claude-<name>` that contains
102
+ both `projects/` and `sessions/`. Each one is an account ("seat" in the UI): `~/.claude` is called `main`,
103
+ `~/.claude-work` is called `work`. Replies, stop and new chats run through the unmodified `claude` binary with that
104
+ account's `CLAUDE_CONFIG_DIR`.
105
+
106
+ Labels come from the directory name (`work` is shown as "Work"). Override them, and mark accounts as read-only
107
+ (shown, never acted on), in `config.json` in the data directory. Copy `config.example.json` to start.
108
+
109
+ | Key | Default | Meaning |
110
+ |---|---|---|
111
+ | `seat_labels` | `{}` | account name to display label |
112
+ | `read_only_accounts` | `[]` | accounts that are shown but never replied to, stopped, spawned on or hooked |
113
+ | `work_item_pattern` | `""` | regex; a chat whose name matches it belongs to that work item (for example `PROJ-\d+`). Empty: no work items, chats group by account and folder |
114
+ | `evidence_gate_work_items` | `[]` | work items whose chats get the evidence gate on by default |
115
+ | `default_cwd` | your home folder | folder for chats started from the page |
116
+ | `launchd_prefix` | `com.dhi.orbit` | label prefix of the launchd jobs the Schedules panel manages |
117
+ | `timezone` | this computer's | IANA name used to show clock times |
118
+ | `meter_log` | `<data dir>/meter.log` | where usage readings come from (below) |
119
+ | `public_url` | `""` | host name of a tunnel you set up yourself (below) |
120
+ | `hidden_accounts` | `[]` | accounts disconnected in Settings > Accounts (not shown on the board) |
121
+ | `plugins` | `{}` | optional helper modules, for example `{"usage_live": {"path": "/dir", "module": "usage_live"}}` |
122
+
123
+ The binary is found from `$CLAUDE_BIN`, then `PATH`, then `~/.local/bin/claude`.
124
+
125
+ ## Usage limits
126
+
127
+ DHI Orbit does not call any usage endpoint. Claude Code hands its status line command each account's limits
128
+ (`rate_limits`: `five_hour` and `seven_day`, with `used_percentage` and `resets_at`), for every chat, background ones
129
+ included. DHI Orbit records them with a status line of its own:
130
+
131
+ - **Connecting an account** in Settings > Accounts turns it on when the account has no status line yet.
132
+ - **Any other account** has a **Show usage** button there. If the account already has a status line, DHI Orbit keeps
133
+ it: its own command runs first to record the limits, then yours runs with the same input and its output is what
134
+ your terminal shows. **Turn off**, Disconnect, or signing out `main` put your status line back exactly. A status
135
+ line you changed yourself afterwards is never touched.
136
+ - Claude Code only runs the status line while a chat is open, so a new account shows "waiting for the first chat"
137
+ until it has run one. With the meter off, the board says "usage not connected" instead.
138
+
139
+ The setting is the account's `settings.json` `statusLine` (your previous one is kept in `dhi-orbit-statusline.json`
140
+ beside it). The readings go to a meter log, one tab-separated line per change: `ISO time`, `config dir`,
141
+ `session id`, `rate_limits` JSON. `dhi-orbit-statusline` is the same command, if you prefer to call it from a status
142
+ line script of your own, and `contrib/statusline-meter.py` is a standalone copy that only records.
143
+
144
+ A reading whose reset time has passed shows "?" and "reset since the last reading", never 0%. An optional
145
+ `usage_live` plugin module (`fetch_live(account_dir_name)` returning `{"five": pct, "seven": pct}`) can supply a
146
+ fallback reading; without it nothing is fetched.
147
+
148
+ ## Automatic actions
149
+
150
+ Everything that sends text into a chat on its own has a mode, `off`, `dry-run` or `on`, set in `config.json`
151
+ (or on the Settings page). The default is `dry-run`: it logs what it would do and sends nothing. The modes are
152
+ `limit_resume`, `decision_hook`, `stop_gate`, `shadow_drafts`, `corrections`, `handoff` and `idle_compact`.
153
+ Automatic keep-warm (a tiny ping before a chat's prompt cache expires) is off until you switch it on in the toolbar.
154
+ Permission prompts are never answered automatically.
155
+
156
+ Two optional Claude Code hooks feed the board. They are not installed for you; register them yourself in
157
+ the Claude Code configuration of each account you want covered:
158
+
159
+ - `dhi-orbit-ask-hook`, a `PreToolUse` hook with matcher `AskUserQuestion`: puts a background chat's question on the board.
160
+ - `dhi-orbit-stop-hook`, a `Stop` hook: records a receipt (files changed, checks run) for each turn and, when the
161
+ evidence gate is on, asks the chat for verification evidence before it ends. At most 3 blocks per turn.
162
+
163
+ Both fail open: any error, a read-only account, or a non-background session prints nothing and the chat carries on.
164
+
165
+ ## How a reply is delivered
166
+
167
+ | Chat | Route |
168
+ |---|---|
169
+ | background session, idle or stopped | typed into `claude attach <id>` through a pty, confirmed by reading the prompt back from the transcript |
170
+ | open in a terminal tab | refused: Return does not submit through a paste into Claude's input box, and two writers corrupt a session |
171
+ | closed chat with no background job | `claude --resume <id> --bg "<text>"` (a copy under a new id; the first reply re-caches the context once) |
172
+ | working right now | refused until idle, or queued and sent when it goes idle |
173
+
174
+ ## Security model
175
+
176
+ - The server binds to 127.0.0.1 only. The `Host` header is checked (no DNS rebinding).
177
+ - Every `/api` call needs the token from `<data dir>/.token` (header `X-Token` or `?token=`). The token file is
178
+ created with mode 600 in a mode 700 directory.
179
+ - Sends go only through the unmodified `claude` binary under each account. DHI Orbit makes no network calls of its own,
180
+ except `gh pr view` (if `gh` is installed) to show whether a PR is still open, and, only if you configure the
181
+ identity-header check below, one request to that identity endpoint.
182
+ - Reaching it from other devices is your choice and your setup: put a tunnel of your own in front of the loopback port
183
+ and describe it in `<data dir>/public.json` (`host`, and either `"key_login": true` for a one-time key sign-in that sets a
184
+ 30-day cookie, or `team_domain` plus `emails` for an identity-header check). With no `public.json` and no `public_url`, any other
185
+ `Host` is refused. With a host but no sign-in method configured, it stays locked.
186
+
187
+ ## Compatibility
188
+
189
+ DHI Orbit reads undocumented Claude Code internals. These are not a stable interface and may change in any Claude Code update,
190
+ which can break parts of DHI Orbit:
191
+
192
+ - `<config dir>/sessions/<pid>.json` (live session records) and `<config dir>/jobs/<id>/state.json` (background jobs),
193
+ - the transcripts, `<config dir>/projects/*/*.jsonl`,
194
+ - the screen output of `claude attach` and `claude logs`, parsed to read and answer permission prompts and questions.
195
+
196
+ Tested with Claude Code 2.1.289 on macOS. If something looks wrong after an update, the board may be showing stale or
197
+ missing data; limits and health show UNKNOWN rather than OK when a source cannot be read.
198
+
199
+ macOS-only parts: opening a chat in Terminal and notifications use `osascript`, and the Schedules panel uses `launchctl`.
200
+ DHI Orbit is developed on macOS; a clean pipx install on Linux was tested end to end (accounts, sign-in, board, nebula).
201
+
202
+ ## Development
203
+
204
+ ```sh
205
+ python3 -m pytest -q
206
+ ```
207
+
208
+ `tools/mock/server.py` serves the UI with synthetic data (no real chats are read); `tools/acceptance/` holds the UI
209
+ checks. `docs/DESIGN.md` records the visual design.
210
+
211
+ ## About
212
+
213
+ Built by Dev Sanghvi at DHI (https://dhi-tech.com).
214
+
215
+ ## License
216
+
217
+ MIT, see `LICENSE`.
@@ -0,0 +1,201 @@
1
+ # DHI Orbit
2
+
3
+ A local web dashboard that shows every Claude Code chat across one or more config directories on one page.
4
+
5
+ - A board of what needs you: questions, permission prompts, blocked jobs, and chats stalled on a usage limit.
6
+ - Reply to a chat from the page (one normal turn in that chat), stop it, or open it in Terminal.
7
+ - Usage limits per account (5-hour and 7-day), set up when you connect the account; never a guess.
8
+ - A graph of accounts, chats, work items and the files they touch.
9
+ - Optional WebGL "nebula" look (Settings, Look). The default look is plain and needs no GPU.
10
+ - Standard library only: no dependencies, no build step, no CDN.
11
+
12
+ Viewing costs zero model tokens: DHI Orbit only reads files. A reply you send is one normal turn in that chat.
13
+
14
+ ## Screenshots
15
+
16
+ Screenshots are not included yet (placeholder).
17
+
18
+ ## Install
19
+
20
+ Requires Python 3.10 or newer.
21
+
22
+ Homebrew (macOS or Linux):
23
+
24
+ ```sh
25
+ brew install Dv04/dhi-orbit/dhi-orbit
26
+ ```
27
+
28
+ pipx:
29
+
30
+ ```sh
31
+ pipx install git+https://github.com/Dv04/dhi-orbit
32
+ ```
33
+
34
+ pip, in any virtualenv:
35
+
36
+ ```sh
37
+ python3 -m pip install git+https://github.com/Dv04/dhi-orbit
38
+ ```
39
+
40
+ You also need Claude Code itself (`claude` on your `PATH`).
41
+
42
+ ## Quick start
43
+
44
+ ```sh
45
+ dhi-orbit
46
+ ```
47
+
48
+ `orbit` is a shorter name for the same command. Then open <http://127.0.0.1:8787/v2/>. `dhi-orbit --help` lists the options (`--port`, `--window-hours`, `--every`, `--no-notify`). macOS notifications for chats that need you are on by default; `--no-notify` turns them off.
49
+
50
+ On first run DHI Orbit creates its data directory, `~/.config/dhi-orbit` (or `$DHI_ORBIT_HOME`), with mode 700, and an
51
+ access token inside it (`.token`, mode 600). The page gets the token injected when it is served from loopback.
52
+
53
+ ### Upgrading from chatdash
54
+
55
+ DHI Orbit is the new name of chatdash. The `chatdash` command still works (as do `chatdash-ask-hook`,
56
+ `chatdash-stop-hook` and `chatdash-statusline`), an existing `~/.config/chatdash` data directory (with its
57
+ `chatdash.db`) keeps being used, and the `CHATDASH_*` environment variables are still read; the `DHI_ORBIT_*`
58
+ name wins when both are set. A status line meter or hook that chatdash installed keeps running (a small
59
+ `chatdash` module forwards to DHI Orbit) and the meter is rewritten to the new command the next time you turn
60
+ usage on for that account. While the old data directory is in use, the Schedules panel keeps managing your
61
+ `com.chatdash.*` launchd jobs; new installs use `com.dhi.orbit`. Python imports are now `dhi_orbit`.
62
+
63
+ ## Connect your accounts
64
+
65
+ Open **Settings > Accounts** (a fresh install opens on that screen). Type a name, for example `work`, and press
66
+ **Connect account**. DHI Orbit creates `~/.claude-work` and runs Claude Code's own `claude auth login` for it:
67
+ a claude.com sign-in page opens in this computer's browser, you approve, and the account shows as signed in.
68
+ From another device (the page on your phone, say), open the sign-in link shown there instead, approve, and paste
69
+ the code claude.com shows; it is typed into Claude Code's own prompt.
70
+
71
+ DHI Orbit never sees, stores or sends your password or tokens: Claude Code writes its credentials into that
72
+ account's folder exactly as when you sign in in a terminal. Accounts you already use (`~/.claude`, any
73
+ `~/.claude-<name>`) appear by themselves.
74
+
75
+ - **Disconnect** hides an account from the board; its sign-in and chats are untouched. **Reconnect** shows it again.
76
+ - **Delete** signs the account out (`claude auth logout`) and moves its folder to the Trash. It needs you to type
77
+ the account name, and refuses while a chat on it is running. `~/.claude` (your default) is only signed out and
78
+ hidden, never moved.
79
+
80
+ Use an account in a terminal with `CLAUDE_CONFIG_DIR=~/.claude-work claude`, or start chats from the board.
81
+ Only connect accounts that are yours; Anthropic's terms do not allow sharing logins.
82
+
83
+ ## Multi-account
84
+
85
+ DHI Orbit looks for config directories in your home folder: `~/.claude` and any `~/.claude-<name>` that contains
86
+ both `projects/` and `sessions/`. Each one is an account ("seat" in the UI): `~/.claude` is called `main`,
87
+ `~/.claude-work` is called `work`. Replies, stop and new chats run through the unmodified `claude` binary with that
88
+ account's `CLAUDE_CONFIG_DIR`.
89
+
90
+ Labels come from the directory name (`work` is shown as "Work"). Override them, and mark accounts as read-only
91
+ (shown, never acted on), in `config.json` in the data directory. Copy `config.example.json` to start.
92
+
93
+ | Key | Default | Meaning |
94
+ |---|---|---|
95
+ | `seat_labels` | `{}` | account name to display label |
96
+ | `read_only_accounts` | `[]` | accounts that are shown but never replied to, stopped, spawned on or hooked |
97
+ | `work_item_pattern` | `""` | regex; a chat whose name matches it belongs to that work item (for example `PROJ-\d+`). Empty: no work items, chats group by account and folder |
98
+ | `evidence_gate_work_items` | `[]` | work items whose chats get the evidence gate on by default |
99
+ | `default_cwd` | your home folder | folder for chats started from the page |
100
+ | `launchd_prefix` | `com.dhi.orbit` | label prefix of the launchd jobs the Schedules panel manages |
101
+ | `timezone` | this computer's | IANA name used to show clock times |
102
+ | `meter_log` | `<data dir>/meter.log` | where usage readings come from (below) |
103
+ | `public_url` | `""` | host name of a tunnel you set up yourself (below) |
104
+ | `hidden_accounts` | `[]` | accounts disconnected in Settings > Accounts (not shown on the board) |
105
+ | `plugins` | `{}` | optional helper modules, for example `{"usage_live": {"path": "/dir", "module": "usage_live"}}` |
106
+
107
+ The binary is found from `$CLAUDE_BIN`, then `PATH`, then `~/.local/bin/claude`.
108
+
109
+ ## Usage limits
110
+
111
+ DHI Orbit does not call any usage endpoint. Claude Code hands its status line command each account's limits
112
+ (`rate_limits`: `five_hour` and `seven_day`, with `used_percentage` and `resets_at`), for every chat, background ones
113
+ included. DHI Orbit records them with a status line of its own:
114
+
115
+ - **Connecting an account** in Settings > Accounts turns it on when the account has no status line yet.
116
+ - **Any other account** has a **Show usage** button there. If the account already has a status line, DHI Orbit keeps
117
+ it: its own command runs first to record the limits, then yours runs with the same input and its output is what
118
+ your terminal shows. **Turn off**, Disconnect, or signing out `main` put your status line back exactly. A status
119
+ line you changed yourself afterwards is never touched.
120
+ - Claude Code only runs the status line while a chat is open, so a new account shows "waiting for the first chat"
121
+ until it has run one. With the meter off, the board says "usage not connected" instead.
122
+
123
+ The setting is the account's `settings.json` `statusLine` (your previous one is kept in `dhi-orbit-statusline.json`
124
+ beside it). The readings go to a meter log, one tab-separated line per change: `ISO time`, `config dir`,
125
+ `session id`, `rate_limits` JSON. `dhi-orbit-statusline` is the same command, if you prefer to call it from a status
126
+ line script of your own, and `contrib/statusline-meter.py` is a standalone copy that only records.
127
+
128
+ A reading whose reset time has passed shows "?" and "reset since the last reading", never 0%. An optional
129
+ `usage_live` plugin module (`fetch_live(account_dir_name)` returning `{"five": pct, "seven": pct}`) can supply a
130
+ fallback reading; without it nothing is fetched.
131
+
132
+ ## Automatic actions
133
+
134
+ Everything that sends text into a chat on its own has a mode, `off`, `dry-run` or `on`, set in `config.json`
135
+ (or on the Settings page). The default is `dry-run`: it logs what it would do and sends nothing. The modes are
136
+ `limit_resume`, `decision_hook`, `stop_gate`, `shadow_drafts`, `corrections`, `handoff` and `idle_compact`.
137
+ Automatic keep-warm (a tiny ping before a chat's prompt cache expires) is off until you switch it on in the toolbar.
138
+ Permission prompts are never answered automatically.
139
+
140
+ Two optional Claude Code hooks feed the board. They are not installed for you; register them yourself in
141
+ the Claude Code configuration of each account you want covered:
142
+
143
+ - `dhi-orbit-ask-hook`, a `PreToolUse` hook with matcher `AskUserQuestion`: puts a background chat's question on the board.
144
+ - `dhi-orbit-stop-hook`, a `Stop` hook: records a receipt (files changed, checks run) for each turn and, when the
145
+ evidence gate is on, asks the chat for verification evidence before it ends. At most 3 blocks per turn.
146
+
147
+ Both fail open: any error, a read-only account, or a non-background session prints nothing and the chat carries on.
148
+
149
+ ## How a reply is delivered
150
+
151
+ | Chat | Route |
152
+ |---|---|
153
+ | background session, idle or stopped | typed into `claude attach <id>` through a pty, confirmed by reading the prompt back from the transcript |
154
+ | open in a terminal tab | refused: Return does not submit through a paste into Claude's input box, and two writers corrupt a session |
155
+ | closed chat with no background job | `claude --resume <id> --bg "<text>"` (a copy under a new id; the first reply re-caches the context once) |
156
+ | working right now | refused until idle, or queued and sent when it goes idle |
157
+
158
+ ## Security model
159
+
160
+ - The server binds to 127.0.0.1 only. The `Host` header is checked (no DNS rebinding).
161
+ - Every `/api` call needs the token from `<data dir>/.token` (header `X-Token` or `?token=`). The token file is
162
+ created with mode 600 in a mode 700 directory.
163
+ - Sends go only through the unmodified `claude` binary under each account. DHI Orbit makes no network calls of its own,
164
+ except `gh pr view` (if `gh` is installed) to show whether a PR is still open, and, only if you configure the
165
+ identity-header check below, one request to that identity endpoint.
166
+ - Reaching it from other devices is your choice and your setup: put a tunnel of your own in front of the loopback port
167
+ and describe it in `<data dir>/public.json` (`host`, and either `"key_login": true` for a one-time key sign-in that sets a
168
+ 30-day cookie, or `team_domain` plus `emails` for an identity-header check). With no `public.json` and no `public_url`, any other
169
+ `Host` is refused. With a host but no sign-in method configured, it stays locked.
170
+
171
+ ## Compatibility
172
+
173
+ DHI Orbit reads undocumented Claude Code internals. These are not a stable interface and may change in any Claude Code update,
174
+ which can break parts of DHI Orbit:
175
+
176
+ - `<config dir>/sessions/<pid>.json` (live session records) and `<config dir>/jobs/<id>/state.json` (background jobs),
177
+ - the transcripts, `<config dir>/projects/*/*.jsonl`,
178
+ - the screen output of `claude attach` and `claude logs`, parsed to read and answer permission prompts and questions.
179
+
180
+ Tested with Claude Code 2.1.289 on macOS. If something looks wrong after an update, the board may be showing stale or
181
+ missing data; limits and health show UNKNOWN rather than OK when a source cannot be read.
182
+
183
+ macOS-only parts: opening a chat in Terminal and notifications use `osascript`, and the Schedules panel uses `launchctl`.
184
+ DHI Orbit is developed on macOS; a clean pipx install on Linux was tested end to end (accounts, sign-in, board, nebula).
185
+
186
+ ## Development
187
+
188
+ ```sh
189
+ python3 -m pytest -q
190
+ ```
191
+
192
+ `tools/mock/server.py` serves the UI with synthetic data (no real chats are read); `tools/acceptance/` holds the UI
193
+ checks. `docs/DESIGN.md` records the visual design.
194
+
195
+ ## About
196
+
197
+ Built by Dev Sanghvi at DHI (https://dhi-tech.com).
198
+
199
+ ## License
200
+
201
+ MIT, see `LICENSE`.
@@ -0,0 +1,2 @@
1
+ """Compatibility shim: chatdash was renamed DHI Orbit (package dhi_orbit). Status lines and hooks written by
2
+ chatdash run `python -m chatdash.statusline` or `python -m chatdash.cp.hooks.*`; these modules forward them."""
File without changes
File without changes
@@ -0,0 +1,5 @@
1
+ """Forwards the old `python -m chatdash.cp.hooks.ask_hook` hook command to dhi_orbit.cp.hooks.ask_hook."""
2
+ import runpy
3
+
4
+ if __name__ == "__main__":
5
+ runpy.run_module("dhi_orbit.cp.hooks.ask_hook", run_name="__main__")
@@ -0,0 +1,5 @@
1
+ """Forwards the old `python -m chatdash.cp.hooks.stop_hook` hook command to dhi_orbit.cp.hooks.stop_hook."""
2
+ import runpy
3
+
4
+ if __name__ == "__main__":
5
+ runpy.run_module("dhi_orbit.cp.hooks.stop_hook", run_name="__main__")
@@ -0,0 +1,7 @@
1
+ """Forwards the old `python -m chatdash.statusline` status line command to dhi_orbit.statusline."""
2
+ import sys
3
+
4
+ from dhi_orbit.statusline import main
5
+
6
+ if __name__ == "__main__":
7
+ sys.exit(main())
@@ -0,0 +1,2 @@
1
+ """DHI Orbit: a local dashboard for every Claude Code chat across your config dirs."""
2
+ __version__ = "0.3.0"
@@ -0,0 +1,4 @@
1
+ from .server import main
2
+
3
+ if __name__ == "__main__":
4
+ main()