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.
- dhi_orbit-0.3.0/LICENSE +21 -0
- dhi_orbit-0.3.0/PKG-INFO +217 -0
- dhi_orbit-0.3.0/README.md +201 -0
- dhi_orbit-0.3.0/chatdash/__init__.py +2 -0
- dhi_orbit-0.3.0/chatdash/cp/__init__.py +0 -0
- dhi_orbit-0.3.0/chatdash/cp/hooks/__init__.py +0 -0
- dhi_orbit-0.3.0/chatdash/cp/hooks/ask_hook.py +5 -0
- dhi_orbit-0.3.0/chatdash/cp/hooks/stop_hook.py +5 -0
- dhi_orbit-0.3.0/chatdash/statusline.py +7 -0
- dhi_orbit-0.3.0/dhi_orbit/__init__.py +2 -0
- dhi_orbit-0.3.0/dhi_orbit/__main__.py +4 -0
- dhi_orbit-0.3.0/dhi_orbit/accounts.py +350 -0
- dhi_orbit-0.3.0/dhi_orbit/actions.py +416 -0
- dhi_orbit-0.3.0/dhi_orbit/collector.py +344 -0
- dhi_orbit-0.3.0/dhi_orbit/config.py +237 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/__init__.py +5 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/api.py +810 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/autolock.py +31 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/corrections.py +125 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/db.py +143 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/decisions.py +239 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/devserver.py +192 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/drafts.py +164 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/fileindex.py +201 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/graphx.py +313 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/hooks/__init__.py +0 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/hooks/ask_hook.py +129 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/hooks/stop_hook.py +115 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/idlecompact.py +287 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/intent.py +173 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/limits.py +64 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/mount.py +130 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/notify.py +80 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/receipts.py +243 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/resume.py +182 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/risk.py +42 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/send.py +65 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/shadow.py +131 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/sources.py +296 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/transcript.py +140 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/work.py +526 -0
- dhi_orbit-0.3.0/dhi_orbit/cp/workitems.py +19 -0
- dhi_orbit-0.3.0/dhi_orbit/extract.py +236 -0
- dhi_orbit-0.3.0/dhi_orbit/index.py +61 -0
- dhi_orbit-0.3.0/dhi_orbit/keepwarm.py +376 -0
- dhi_orbit-0.3.0/dhi_orbit/panels.py +120 -0
- dhi_orbit-0.3.0/dhi_orbit/server.py +531 -0
- dhi_orbit-0.3.0/dhi_orbit/static/index.html +358 -0
- dhi_orbit-0.3.0/dhi_orbit/statusline.py +83 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/css/app.css +698 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/css/nebula.css +636 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/icon.svg +6 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/index.html +52 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/accounts.js +114 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/answer.js +80 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/api.js +54 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/app.js +435 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/board.js +380 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/capacity.js +154 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/cards.js +221 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/chat.js +218 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/dictate.js +108 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/field.js +473 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/gl-nebula.js +463 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/gl-worker.js +20 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/graph.js +822 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/gx.js +303 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/lib.js +114 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/md.js +100 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/nebula.js +158 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/palette.js +100 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/settings.js +147 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/sky.js +365 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/spawn.js +72 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/voice.js +185 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/js/workitem.js +113 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/manifest.webmanifest +10 -0
- dhi_orbit-0.3.0/dhi_orbit/ui/sw.js +17 -0
- dhi_orbit-0.3.0/dhi_orbit/usage_meter.py +185 -0
- dhi_orbit-0.3.0/dhi_orbit.egg-info/PKG-INFO +217 -0
- dhi_orbit-0.3.0/dhi_orbit.egg-info/SOURCES.txt +98 -0
- dhi_orbit-0.3.0/dhi_orbit.egg-info/dependency_links.txt +1 -0
- dhi_orbit-0.3.0/dhi_orbit.egg-info/entry_points.txt +10 -0
- dhi_orbit-0.3.0/dhi_orbit.egg-info/top_level.txt +2 -0
- dhi_orbit-0.3.0/pyproject.toml +45 -0
- dhi_orbit-0.3.0/setup.cfg +4 -0
- dhi_orbit-0.3.0/tests/test_accounts.py +115 -0
- dhi_orbit-0.3.0/tests/test_config.py +232 -0
- dhi_orbit-0.3.0/tests/test_cp.py +446 -0
- dhi_orbit-0.3.0/tests/test_cp_decisions.py +226 -0
- dhi_orbit-0.3.0/tests/test_cp_drafts_corrections.py +102 -0
- dhi_orbit-0.3.0/tests/test_cp_graphx.py +140 -0
- dhi_orbit-0.3.0/tests/test_cp_receipts.py +166 -0
- dhi_orbit-0.3.0/tests/test_cp_resume.py +319 -0
- dhi_orbit-0.3.0/tests/test_cp_shadow.py +62 -0
- dhi_orbit-0.3.0/tests/test_cp_work.py +199 -0
- dhi_orbit-0.3.0/tests/test_dhi_orbit.py +173 -0
- dhi_orbit-0.3.0/tests/test_keepwarm.py +353 -0
- dhi_orbit-0.3.0/tests/test_meter_script.py +47 -0
- dhi_orbit-0.3.0/tests/test_usage_meter.py +197 -0
dhi_orbit-0.3.0/LICENSE
ADDED
|
@@ -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.
|
dhi_orbit-0.3.0/PKG-INFO
ADDED
|
@@ -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`.
|
|
File without changes
|
|
File without changes
|