swarm-debug 0.1.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.
- swarm_debug-0.1.0/PKG-INFO +237 -0
- swarm_debug-0.1.0/README.md +224 -0
- swarm_debug-0.1.0/backend/__init__.py +0 -0
- swarm_debug-0.1.0/backend/apps/__init__.py +0 -0
- swarm_debug-0.1.0/backend/apps/debugger/__init__.py +0 -0
- swarm_debug-0.1.0/backend/apps/debugger/debugger.py +83 -0
- swarm_debug-0.1.0/backend/apps/health/__init__.py +0 -0
- swarm_debug-0.1.0/backend/apps/health/health.py +33 -0
- swarm_debug-0.1.0/backend/config/Apps.py +48 -0
- swarm_debug-0.1.0/backend/config/__init__.py +0 -0
- swarm_debug-0.1.0/backend/core/DEFAULTS.py +29 -0
- swarm_debug-0.1.0/backend/core/Debugleton.py +87 -0
- swarm_debug-0.1.0/backend/core/__init__.py +0 -0
- swarm_debug-0.1.0/backend/core/data_dir.py +30 -0
- swarm_debug-0.1.0/backend/core/log/__init__.py +2 -0
- swarm_debug-0.1.0/backend/core/log/log_config.py +46 -0
- swarm_debug-0.1.0/backend/core/log/log_mode.py +13 -0
- swarm_debug-0.1.0/backend/core/models/DebugFile.py +28 -0
- swarm_debug-0.1.0/backend/core/models/Directory.py +216 -0
- swarm_debug-0.1.0/backend/core/models/File.py +29 -0
- swarm_debug-0.1.0/backend/core/models/__init__.py +4 -0
- swarm_debug-0.1.0/backend/core/models/project_scanner.py +153 -0
- swarm_debug-0.1.0/backend/core/utils/__init__.py +3 -0
- swarm_debug-0.1.0/backend/core/utils/color_adjuster.py +13 -0
- swarm_debug-0.1.0/backend/core/utils/debug_arg_parser.py +22 -0
- swarm_debug-0.1.0/backend/core/utils/path_mngr.py +15 -0
- swarm_debug-0.1.0/backend/debugger_gui_build/bundle.js +102 -0
- swarm_debug-0.1.0/backend/debugger_gui_build/bundle.js.LICENSE.txt +68 -0
- swarm_debug-0.1.0/backend/debugger_gui_build/index.html +1 -0
- swarm_debug-0.1.0/backend/main.py +40 -0
- swarm_debug-0.1.0/debug.py +57 -0
- swarm_debug-0.1.0/pyproject.toml +39 -0
- swarm_debug-0.1.0/setup.cfg +4 -0
- swarm_debug-0.1.0/swarm_debug.egg-info/PKG-INFO +237 -0
- swarm_debug-0.1.0/swarm_debug.egg-info/SOURCES.txt +37 -0
- swarm_debug-0.1.0/swarm_debug.egg-info/dependency_links.txt +1 -0
- swarm_debug-0.1.0/swarm_debug.egg-info/entry_points.txt +2 -0
- swarm_debug-0.1.0/swarm_debug.egg-info/requires.txt +6 -0
- swarm_debug-0.1.0/swarm_debug.egg-info/top_level.txt +2 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: swarm-debug
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A colorized, toggleable debug logger with a web GUI
|
|
5
|
+
License: MIT
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: fastapi[standard]
|
|
9
|
+
Requires-Dist: typeguard>=4.4
|
|
10
|
+
Requires-Dist: uvicorn[standard]
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: vulture; extra == "dev"
|
|
13
|
+
|
|
14
|
+
# swarm-debug
|
|
15
|
+
|
|
16
|
+
A drop-in replacement for `print()` debugging. Colorized, per-file toggleable output with a visual web GUI to control it all.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install swarm-debug
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## What it does
|
|
23
|
+
|
|
24
|
+
`debug()` works like `print()`, but every call is:
|
|
25
|
+
|
|
26
|
+
- **Colorized** -- each file gets its own color so you can visually separate output
|
|
27
|
+
- **Toggleable** -- turn debug output on/off per file or entire directories without touching code
|
|
28
|
+
- **Context-aware** -- automatically shows the calling function, class, variable name, and indentation level
|
|
29
|
+
- **Emoji-tagged** -- assign emojis to files for instant visual scanning
|
|
30
|
+
- **Error-aware** -- exceptions are auto-highlighted in red with a dedicated emoji
|
|
31
|
+
|
|
32
|
+
All configuration is managed through a web GUI. No config files to write, no decorators to add.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### 1. Add `debug()` calls to your code
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
import debug
|
|
40
|
+
|
|
41
|
+
x = 42
|
|
42
|
+
debug(x)
|
|
43
|
+
# ⚫ [my_script.py] : x = 42
|
|
44
|
+
|
|
45
|
+
debug("loading config")
|
|
46
|
+
# ⚫ [my_script.py] : loading config
|
|
47
|
+
|
|
48
|
+
def process(data):
|
|
49
|
+
debug(data, len(data))
|
|
50
|
+
# ⚫ [MyClass.process] : data = [1, 2, 3]
|
|
51
|
+
# ⚫ [MyClass.process] : len(data) = 3
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Strings are rendered as italic labels. Everything else shows `name = value`. Errors are auto-detected and forced on in red regardless of toggle state.
|
|
55
|
+
|
|
56
|
+
### 2. Launch the GUI
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
debug-server
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Open [http://localhost:8324](http://localhost:8324). You'll see a file tree of your project showing every file that calls `debug()`. From there you can:
|
|
63
|
+
|
|
64
|
+
- Toggle files/directories on and off
|
|
65
|
+
- Assign custom colors per file or directory (children inherit from parents)
|
|
66
|
+
- Assign emojis for visual tagging
|
|
67
|
+
- Push/pull configuration changes
|
|
68
|
+
- Reset colors or emojis to defaults
|
|
69
|
+
|
|
70
|
+
The server scans whichever directory you launched it from. To point it at a different project:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Option A: cd into the project first
|
|
74
|
+
cd /path/to/my/project && debug-server
|
|
75
|
+
|
|
76
|
+
# Option B: set an env var
|
|
77
|
+
SWARM_DEBUG_ROOT=/path/to/my/project debug-server
|
|
78
|
+
|
|
79
|
+
# Option C: use the API
|
|
80
|
+
curl -X POST http://localhost:8324/api/debugger/root_dir \
|
|
81
|
+
-H "Content-Type: application/json" \
|
|
82
|
+
-d '{"root_dir": "/path/to/my/project"}'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The root dir persists across restarts (saved to `~/.swarm-debug/root_dir.txt`).
|
|
86
|
+
|
|
87
|
+
### Configuration storage
|
|
88
|
+
|
|
89
|
+
All runtime state lives in `~/.swarm-debug/`:
|
|
90
|
+
|
|
91
|
+
| File | Purpose |
|
|
92
|
+
|------|---------|
|
|
93
|
+
| `debug_toggles.json` | Per-file toggle, color, and emoji state |
|
|
94
|
+
| `root_dir.txt` | Persisted project root directory |
|
|
95
|
+
| `log_mode.txt` | Log output mode (`all`, `debug`, etc.) |
|
|
96
|
+
| `needs_resync.txt` | Internal flag for syncing state |
|
|
97
|
+
|
|
98
|
+
### API endpoints
|
|
99
|
+
|
|
100
|
+
| Method | Endpoint | Description |
|
|
101
|
+
|--------|----------|-------------|
|
|
102
|
+
| GET | `/api/health/check` | Health check |
|
|
103
|
+
| GET | `/api/debugger/pull_structure` | Get file tree with toggle states |
|
|
104
|
+
| POST | `/api/debugger/push_structure` | Save toggle/color/emoji config |
|
|
105
|
+
| POST | `/api/debugger/reset_color` | Reset all colors to defaults |
|
|
106
|
+
| POST | `/api/debugger/reset_emoji` | Reset all emojis to defaults |
|
|
107
|
+
| GET | `/api/debugger/root_dir` | Get current project root |
|
|
108
|
+
| POST | `/api/debugger/root_dir` | Set project root (triggers resync) |
|
|
109
|
+
|
|
110
|
+
Full interactive docs at [http://localhost:8324/docs](http://localhost:8324/docs).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Development (source)
|
|
115
|
+
|
|
116
|
+
### Prerequisites
|
|
117
|
+
|
|
118
|
+
- Python 3.9+
|
|
119
|
+
- Node.js 18+
|
|
120
|
+
|
|
121
|
+
### Running locally
|
|
122
|
+
|
|
123
|
+
Both services (backend on `:8324`, frontend dev server on `:3000`):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
bash run.sh
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Individually:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
bash backend/run.sh # FastAPI backend only
|
|
133
|
+
bash frontend/run.sh # Webpack dev server only
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Tech stack
|
|
137
|
+
|
|
138
|
+
| Layer | Tech |
|
|
139
|
+
|-------|------|
|
|
140
|
+
| Frontend | React 18, TypeScript, Webpack 5, MUI v7, Redux Toolkit, Framer Motion |
|
|
141
|
+
| Backend | FastAPI, Uvicorn, Python 3.9+ |
|
|
142
|
+
| Runtime types | typeguard (`@typechecked` on endpoints) |
|
|
143
|
+
|
|
144
|
+
### Architecture
|
|
145
|
+
|
|
146
|
+
**Backend** uses a SubApp pattern -- each feature is a self-contained module with its own APIRouter and async lifespan, auto-mounted at `/api/{name}/`. SubApps are registered in `backend/config/Apps.py` and composed into the FastAPI app in `backend/main.py`.
|
|
147
|
+
|
|
148
|
+
**Frontend** uses a custom design token system layered on MUI, accessed via `useClaudeTokens()`. See `frontend/DESIGN.md` for the full spec.
|
|
149
|
+
|
|
150
|
+
**Debugleton** is a thread-safe singleton that holds the scanned project tree in memory and resyncs when the `needs_resync` flag is set (after any push from the GUI).
|
|
151
|
+
|
|
152
|
+
### Project structure
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
debugger/
|
|
156
|
+
├── debug.py # The debug() function -- pip module entry point
|
|
157
|
+
├── pyproject.toml # PyPI package config (swarm-debug)
|
|
158
|
+
├── publish.sh # Build + publish to PyPI
|
|
159
|
+
├── run.sh # Dev orchestrator: backend -> frontend
|
|
160
|
+
├── ports.conf # Port configuration
|
|
161
|
+
├── backend/
|
|
162
|
+
│ ├── main.py # FastAPI app, CORS, static file serving
|
|
163
|
+
│ ├── config/Apps.py # SubApp / MainApp framework
|
|
164
|
+
│ ├── apps/
|
|
165
|
+
│ │ ├── health/health.py # GET /api/health/check
|
|
166
|
+
│ │ └── debugger/debugger.py # All debugger API endpoints
|
|
167
|
+
│ ├── core/
|
|
168
|
+
│ │ ├── data_dir.py # ~/.swarm-debug/ path management
|
|
169
|
+
│ │ ├── DEFAULTS.py # Default values, get/set_root_dir
|
|
170
|
+
│ │ ├── Debugleton.py # Thread-safe singleton for project state
|
|
171
|
+
│ │ ├── models/
|
|
172
|
+
│ │ │ ├── File.py # Base file class
|
|
173
|
+
│ │ │ ├── DebugFile.py # File with color/toggle/emoji
|
|
174
|
+
│ │ │ ├── Directory.py # Recursive directory tree
|
|
175
|
+
│ │ │ └── project_scanner.py
|
|
176
|
+
│ │ ├── log/
|
|
177
|
+
│ │ │ ├── log_config.py # Custom logger with modes
|
|
178
|
+
│ │ │ └── log_mode.py # Read/write log mode
|
|
179
|
+
│ │ └── utils/
|
|
180
|
+
│ │ ├── color_adjuster.py
|
|
181
|
+
│ │ ├── debug_arg_parser.py
|
|
182
|
+
│ │ └── path_mngr.py
|
|
183
|
+
│ └── data/ # Legacy data dir (runtime state now in ~/.swarm-debug/)
|
|
184
|
+
└── frontend/
|
|
185
|
+
├── package.json
|
|
186
|
+
├── webpack.config.js
|
|
187
|
+
├── DESIGN.md # Design system specification
|
|
188
|
+
└── src/
|
|
189
|
+
├── index.tsx
|
|
190
|
+
├── app/
|
|
191
|
+
│ ├── Main.tsx
|
|
192
|
+
│ ├── pages/Debugger/
|
|
193
|
+
│ └── components/ # Tree, SyncSection, EmojiPicker, SettingsModal
|
|
194
|
+
└── shared/
|
|
195
|
+
├── state/ # Redux store, slice, thunks
|
|
196
|
+
├── styles/ # Theme tokens
|
|
197
|
+
└── constants/
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Publishing to PyPI
|
|
203
|
+
|
|
204
|
+
Everything is handled by a single script:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
# Publish to test.pypi.org (for testing)
|
|
208
|
+
./publish.sh --test
|
|
209
|
+
|
|
210
|
+
# Publish to pypi.org (for real)
|
|
211
|
+
./publish.sh --real
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The script will:
|
|
215
|
+
1. Clean previous build artifacts
|
|
216
|
+
2. Build the React frontend (`npm ci && npm run build`)
|
|
217
|
+
3. Bundle the build into `backend/debugger_gui_build/`
|
|
218
|
+
4. Build the Python sdist + wheel
|
|
219
|
+
5. Upload via twine
|
|
220
|
+
|
|
221
|
+
### Prerequisites for publishing
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
pip install build twine
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
You'll need a PyPI account and API token. Configure `~/.pypirc` or pass credentials when prompted by twine.
|
|
228
|
+
|
|
229
|
+
### How the pip package works
|
|
230
|
+
|
|
231
|
+
When installed from PyPI, the pre-built React frontend is bundled inside the wheel at `backend/debugger_gui_build/`. The FastAPI server serves these static files alongside the API, so end users get both the GUI and the API on a single port (8324) with zero Node.js dependency.
|
|
232
|
+
|
|
233
|
+
The `debug` module uses a `sys.modules` trick to make itself callable -- `import debug` gives you a function, not a module. This means `debug(x)` works directly after import with no extra setup.
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
MIT
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# swarm-debug
|
|
2
|
+
|
|
3
|
+
A drop-in replacement for `print()` debugging. Colorized, per-file toggleable output with a visual web GUI to control it all.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install swarm-debug
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## What it does
|
|
10
|
+
|
|
11
|
+
`debug()` works like `print()`, but every call is:
|
|
12
|
+
|
|
13
|
+
- **Colorized** -- each file gets its own color so you can visually separate output
|
|
14
|
+
- **Toggleable** -- turn debug output on/off per file or entire directories without touching code
|
|
15
|
+
- **Context-aware** -- automatically shows the calling function, class, variable name, and indentation level
|
|
16
|
+
- **Emoji-tagged** -- assign emojis to files for instant visual scanning
|
|
17
|
+
- **Error-aware** -- exceptions are auto-highlighted in red with a dedicated emoji
|
|
18
|
+
|
|
19
|
+
All configuration is managed through a web GUI. No config files to write, no decorators to add.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### 1. Add `debug()` calls to your code
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
import debug
|
|
27
|
+
|
|
28
|
+
x = 42
|
|
29
|
+
debug(x)
|
|
30
|
+
# ⚫ [my_script.py] : x = 42
|
|
31
|
+
|
|
32
|
+
debug("loading config")
|
|
33
|
+
# ⚫ [my_script.py] : loading config
|
|
34
|
+
|
|
35
|
+
def process(data):
|
|
36
|
+
debug(data, len(data))
|
|
37
|
+
# ⚫ [MyClass.process] : data = [1, 2, 3]
|
|
38
|
+
# ⚫ [MyClass.process] : len(data) = 3
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Strings are rendered as italic labels. Everything else shows `name = value`. Errors are auto-detected and forced on in red regardless of toggle state.
|
|
42
|
+
|
|
43
|
+
### 2. Launch the GUI
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
debug-server
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Open [http://localhost:8324](http://localhost:8324). You'll see a file tree of your project showing every file that calls `debug()`. From there you can:
|
|
50
|
+
|
|
51
|
+
- Toggle files/directories on and off
|
|
52
|
+
- Assign custom colors per file or directory (children inherit from parents)
|
|
53
|
+
- Assign emojis for visual tagging
|
|
54
|
+
- Push/pull configuration changes
|
|
55
|
+
- Reset colors or emojis to defaults
|
|
56
|
+
|
|
57
|
+
The server scans whichever directory you launched it from. To point it at a different project:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Option A: cd into the project first
|
|
61
|
+
cd /path/to/my/project && debug-server
|
|
62
|
+
|
|
63
|
+
# Option B: set an env var
|
|
64
|
+
SWARM_DEBUG_ROOT=/path/to/my/project debug-server
|
|
65
|
+
|
|
66
|
+
# Option C: use the API
|
|
67
|
+
curl -X POST http://localhost:8324/api/debugger/root_dir \
|
|
68
|
+
-H "Content-Type: application/json" \
|
|
69
|
+
-d '{"root_dir": "/path/to/my/project"}'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The root dir persists across restarts (saved to `~/.swarm-debug/root_dir.txt`).
|
|
73
|
+
|
|
74
|
+
### Configuration storage
|
|
75
|
+
|
|
76
|
+
All runtime state lives in `~/.swarm-debug/`:
|
|
77
|
+
|
|
78
|
+
| File | Purpose |
|
|
79
|
+
|------|---------|
|
|
80
|
+
| `debug_toggles.json` | Per-file toggle, color, and emoji state |
|
|
81
|
+
| `root_dir.txt` | Persisted project root directory |
|
|
82
|
+
| `log_mode.txt` | Log output mode (`all`, `debug`, etc.) |
|
|
83
|
+
| `needs_resync.txt` | Internal flag for syncing state |
|
|
84
|
+
|
|
85
|
+
### API endpoints
|
|
86
|
+
|
|
87
|
+
| Method | Endpoint | Description |
|
|
88
|
+
|--------|----------|-------------|
|
|
89
|
+
| GET | `/api/health/check` | Health check |
|
|
90
|
+
| GET | `/api/debugger/pull_structure` | Get file tree with toggle states |
|
|
91
|
+
| POST | `/api/debugger/push_structure` | Save toggle/color/emoji config |
|
|
92
|
+
| POST | `/api/debugger/reset_color` | Reset all colors to defaults |
|
|
93
|
+
| POST | `/api/debugger/reset_emoji` | Reset all emojis to defaults |
|
|
94
|
+
| GET | `/api/debugger/root_dir` | Get current project root |
|
|
95
|
+
| POST | `/api/debugger/root_dir` | Set project root (triggers resync) |
|
|
96
|
+
|
|
97
|
+
Full interactive docs at [http://localhost:8324/docs](http://localhost:8324/docs).
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Development (source)
|
|
102
|
+
|
|
103
|
+
### Prerequisites
|
|
104
|
+
|
|
105
|
+
- Python 3.9+
|
|
106
|
+
- Node.js 18+
|
|
107
|
+
|
|
108
|
+
### Running locally
|
|
109
|
+
|
|
110
|
+
Both services (backend on `:8324`, frontend dev server on `:3000`):
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
bash run.sh
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Individually:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
bash backend/run.sh # FastAPI backend only
|
|
120
|
+
bash frontend/run.sh # Webpack dev server only
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Tech stack
|
|
124
|
+
|
|
125
|
+
| Layer | Tech |
|
|
126
|
+
|-------|------|
|
|
127
|
+
| Frontend | React 18, TypeScript, Webpack 5, MUI v7, Redux Toolkit, Framer Motion |
|
|
128
|
+
| Backend | FastAPI, Uvicorn, Python 3.9+ |
|
|
129
|
+
| Runtime types | typeguard (`@typechecked` on endpoints) |
|
|
130
|
+
|
|
131
|
+
### Architecture
|
|
132
|
+
|
|
133
|
+
**Backend** uses a SubApp pattern -- each feature is a self-contained module with its own APIRouter and async lifespan, auto-mounted at `/api/{name}/`. SubApps are registered in `backend/config/Apps.py` and composed into the FastAPI app in `backend/main.py`.
|
|
134
|
+
|
|
135
|
+
**Frontend** uses a custom design token system layered on MUI, accessed via `useClaudeTokens()`. See `frontend/DESIGN.md` for the full spec.
|
|
136
|
+
|
|
137
|
+
**Debugleton** is a thread-safe singleton that holds the scanned project tree in memory and resyncs when the `needs_resync` flag is set (after any push from the GUI).
|
|
138
|
+
|
|
139
|
+
### Project structure
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
debugger/
|
|
143
|
+
├── debug.py # The debug() function -- pip module entry point
|
|
144
|
+
├── pyproject.toml # PyPI package config (swarm-debug)
|
|
145
|
+
├── publish.sh # Build + publish to PyPI
|
|
146
|
+
├── run.sh # Dev orchestrator: backend -> frontend
|
|
147
|
+
├── ports.conf # Port configuration
|
|
148
|
+
├── backend/
|
|
149
|
+
│ ├── main.py # FastAPI app, CORS, static file serving
|
|
150
|
+
│ ├── config/Apps.py # SubApp / MainApp framework
|
|
151
|
+
│ ├── apps/
|
|
152
|
+
│ │ ├── health/health.py # GET /api/health/check
|
|
153
|
+
│ │ └── debugger/debugger.py # All debugger API endpoints
|
|
154
|
+
│ ├── core/
|
|
155
|
+
│ │ ├── data_dir.py # ~/.swarm-debug/ path management
|
|
156
|
+
│ │ ├── DEFAULTS.py # Default values, get/set_root_dir
|
|
157
|
+
│ │ ├── Debugleton.py # Thread-safe singleton for project state
|
|
158
|
+
│ │ ├── models/
|
|
159
|
+
│ │ │ ├── File.py # Base file class
|
|
160
|
+
│ │ │ ├── DebugFile.py # File with color/toggle/emoji
|
|
161
|
+
│ │ │ ├── Directory.py # Recursive directory tree
|
|
162
|
+
│ │ │ └── project_scanner.py
|
|
163
|
+
│ │ ├── log/
|
|
164
|
+
│ │ │ ├── log_config.py # Custom logger with modes
|
|
165
|
+
│ │ │ └── log_mode.py # Read/write log mode
|
|
166
|
+
│ │ └── utils/
|
|
167
|
+
│ │ ├── color_adjuster.py
|
|
168
|
+
│ │ ├── debug_arg_parser.py
|
|
169
|
+
│ │ └── path_mngr.py
|
|
170
|
+
│ └── data/ # Legacy data dir (runtime state now in ~/.swarm-debug/)
|
|
171
|
+
└── frontend/
|
|
172
|
+
├── package.json
|
|
173
|
+
├── webpack.config.js
|
|
174
|
+
├── DESIGN.md # Design system specification
|
|
175
|
+
└── src/
|
|
176
|
+
├── index.tsx
|
|
177
|
+
├── app/
|
|
178
|
+
│ ├── Main.tsx
|
|
179
|
+
│ ├── pages/Debugger/
|
|
180
|
+
│ └── components/ # Tree, SyncSection, EmojiPicker, SettingsModal
|
|
181
|
+
└── shared/
|
|
182
|
+
├── state/ # Redux store, slice, thunks
|
|
183
|
+
├── styles/ # Theme tokens
|
|
184
|
+
└── constants/
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Publishing to PyPI
|
|
190
|
+
|
|
191
|
+
Everything is handled by a single script:
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Publish to test.pypi.org (for testing)
|
|
195
|
+
./publish.sh --test
|
|
196
|
+
|
|
197
|
+
# Publish to pypi.org (for real)
|
|
198
|
+
./publish.sh --real
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The script will:
|
|
202
|
+
1. Clean previous build artifacts
|
|
203
|
+
2. Build the React frontend (`npm ci && npm run build`)
|
|
204
|
+
3. Bundle the build into `backend/debugger_gui_build/`
|
|
205
|
+
4. Build the Python sdist + wheel
|
|
206
|
+
5. Upload via twine
|
|
207
|
+
|
|
208
|
+
### Prerequisites for publishing
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
pip install build twine
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
You'll need a PyPI account and API token. Configure `~/.pypirc` or pass credentials when prompted by twine.
|
|
215
|
+
|
|
216
|
+
### How the pip package works
|
|
217
|
+
|
|
218
|
+
When installed from PyPI, the pre-built React frontend is bundled inside the wheel at `backend/debugger_gui_build/`. The FastAPI server serves these static files alongside the API, so end users get both the GUI and the API on a single port (8324) with zero Node.js dependency.
|
|
219
|
+
|
|
220
|
+
The `debug` module uses a `sys.modules` trick to make itself callable -- `import debug` gives you a function, not a module. This means `debug(x)` works directly after import with no extra setup.
|
|
221
|
+
|
|
222
|
+
## License
|
|
223
|
+
|
|
224
|
+
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from backend.config.Apps import SubApp
|
|
5
|
+
from backend.core.models.project_scanner import update_debug_toggles, dir_to_output_format
|
|
6
|
+
from backend.core.data_dir import NEEDS_RESYNC_FILE, TOGGLE_FILE as DEBUG_TOGGLE_FILE
|
|
7
|
+
from backend.core.DEFAULTS import get_root_dir, set_root_dir
|
|
8
|
+
from contextlib import asynccontextmanager
|
|
9
|
+
from fastapi.responses import JSONResponse
|
|
10
|
+
from typeguard import typechecked
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@asynccontextmanager
|
|
16
|
+
async def debugger_lifespan():
|
|
17
|
+
log.debug("debugger_lifespan START")
|
|
18
|
+
yield
|
|
19
|
+
log.debug("debugger_lifespan END")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
debugger = SubApp("debugger", debugger_lifespan)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@debugger.router.get("/pull_structure")
|
|
26
|
+
@typechecked
|
|
27
|
+
async def pull_structure() -> JSONResponse:
|
|
28
|
+
log.info("GET /api/debugger/pull_structure")
|
|
29
|
+
scanned_dir = update_debug_toggles(save_to_file=True)
|
|
30
|
+
output = dir_to_output_format(scanned_dir)
|
|
31
|
+
return JSONResponse(content=output)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@debugger.router.post("/push_structure")
|
|
35
|
+
@typechecked
|
|
36
|
+
async def push_structure(data: dict) -> JSONResponse:
|
|
37
|
+
log.info("POST /api/debugger/push_structure")
|
|
38
|
+
project_structure = data['projectStructure']
|
|
39
|
+
with open(DEBUG_TOGGLE_FILE, 'w', encoding='utf-8') as file:
|
|
40
|
+
json.dump(project_structure, file, indent=4)
|
|
41
|
+
with open(NEEDS_RESYNC_FILE, 'w') as f:
|
|
42
|
+
f.write('1')
|
|
43
|
+
return JSONResponse(content={"status": "success"})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@debugger.router.post("/reset_color")
|
|
47
|
+
@typechecked
|
|
48
|
+
async def reset_color() -> JSONResponse:
|
|
49
|
+
log.info("POST /api/debugger/reset_color")
|
|
50
|
+
scanned_dir = update_debug_toggles(save_to_file=False)
|
|
51
|
+
scanned_dir.reset_colors()
|
|
52
|
+
output = dir_to_output_format(scanned_dir)
|
|
53
|
+
return JSONResponse(content=output)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@debugger.router.post("/reset_emoji")
|
|
57
|
+
@typechecked
|
|
58
|
+
async def reset_emoji() -> JSONResponse:
|
|
59
|
+
log.info("POST /api/debugger/reset_emoji")
|
|
60
|
+
scanned_dir = update_debug_toggles(save_to_file=False)
|
|
61
|
+
scanned_dir.reset_emojis()
|
|
62
|
+
output = dir_to_output_format(scanned_dir)
|
|
63
|
+
return JSONResponse(content=output)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@debugger.router.get("/root_dir")
|
|
67
|
+
@typechecked
|
|
68
|
+
async def get_root() -> JSONResponse:
|
|
69
|
+
log.info("GET /api/debugger/root_dir")
|
|
70
|
+
return JSONResponse(content={"root_dir": get_root_dir()})
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@debugger.router.post("/root_dir")
|
|
74
|
+
@typechecked
|
|
75
|
+
async def set_root(data: dict) -> JSONResponse:
|
|
76
|
+
log.info("POST /api/debugger/root_dir")
|
|
77
|
+
path = data.get("root_dir", "")
|
|
78
|
+
if not path or not os.path.isdir(path):
|
|
79
|
+
return JSONResponse(content={"error": "Invalid directory path"}, status_code=400)
|
|
80
|
+
set_root_dir(path)
|
|
81
|
+
with open(NEEDS_RESYNC_FILE, 'w') as f:
|
|
82
|
+
f.write('1')
|
|
83
|
+
return JSONResponse(content={"root_dir": get_root_dir()})
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from backend.config.Apps import SubApp
|
|
3
|
+
from contextlib import asynccontextmanager
|
|
4
|
+
from fastapi.responses import PlainTextResponse
|
|
5
|
+
from typeguard import typechecked
|
|
6
|
+
from fastapi import status
|
|
7
|
+
|
|
8
|
+
log = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
@asynccontextmanager
|
|
11
|
+
async def health_lifespan():
|
|
12
|
+
log.debug("health_lifespan START")
|
|
13
|
+
yield
|
|
14
|
+
log.debug("health_lifespan END")
|
|
15
|
+
|
|
16
|
+
health = SubApp("health", health_lifespan)
|
|
17
|
+
|
|
18
|
+
######################################
|
|
19
|
+
# Health Check Endpoints #
|
|
20
|
+
######################################
|
|
21
|
+
|
|
22
|
+
@health.router.get("/check")
|
|
23
|
+
@typechecked
|
|
24
|
+
async def check() -> PlainTextResponse:
|
|
25
|
+
log.info("Health check successful")
|
|
26
|
+
return PlainTextResponse(
|
|
27
|
+
content="OK",
|
|
28
|
+
status_code=status.HTTP_200_OK,
|
|
29
|
+
headers={
|
|
30
|
+
"Content-Type": "text/plain",
|
|
31
|
+
"Content-Length": "2"
|
|
32
|
+
}
|
|
33
|
+
)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
from fastapi import FastAPI, APIRouter
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
from typing import List
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
from contextlib import AsyncExitStack
|
|
8
|
+
from typing import Callable
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SubApp:
|
|
14
|
+
def __init__(self, name:str, lifespan:Callable):
|
|
15
|
+
log.debug("SubApp.__init__ START: %s", name)
|
|
16
|
+
self.id = uuid4()
|
|
17
|
+
self.name = name
|
|
18
|
+
self.prefix = f"/api/{name}"
|
|
19
|
+
self.lifespan = lifespan
|
|
20
|
+
self.router = APIRouter()
|
|
21
|
+
log.debug("SubApp.__init__ END")
|
|
22
|
+
|
|
23
|
+
def __str__(self):
|
|
24
|
+
return f"SubApp(name={self.name}, prefix={self.prefix}, id={self.id})"
|
|
25
|
+
|
|
26
|
+
class MainApp:
|
|
27
|
+
def __init__(self, sub_apps: List[SubApp]):
|
|
28
|
+
log.debug("MainApp.__init__ START")
|
|
29
|
+
|
|
30
|
+
@asynccontextmanager
|
|
31
|
+
async def lifespan(app: FastAPI):
|
|
32
|
+
async with AsyncExitStack() as stack:
|
|
33
|
+
for sub_app in sub_apps:
|
|
34
|
+
log.debug("Starting lifespan for sub_app: %s", sub_app.name)
|
|
35
|
+
await stack.enter_async_context(sub_app.lifespan())
|
|
36
|
+
port = os.environ.get("BACKEND_PORT", "8324")
|
|
37
|
+
print(f"\nCheck out the API docs at: http://127.0.0.1:{port}/docs\n")
|
|
38
|
+
yield
|
|
39
|
+
|
|
40
|
+
self.app = FastAPI(lifespan=lifespan)
|
|
41
|
+
|
|
42
|
+
for sub_app in sub_apps:
|
|
43
|
+
self.app.include_router(
|
|
44
|
+
sub_app.router,
|
|
45
|
+
prefix=sub_app.prefix,
|
|
46
|
+
tags=[sub_app.name]
|
|
47
|
+
)
|
|
48
|
+
log.debug("MainApp.__init__ END")
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from backend.core.data_dir import TOGGLE_FILE, ROOT_DIR_FILE
|
|
3
|
+
|
|
4
|
+
DEFAULT_COLOR = '#ffffff'
|
|
5
|
+
DEFAULT_TOGGLED = False
|
|
6
|
+
DEFAULT_SET_MANUALLY = False
|
|
7
|
+
DEFAULT_SET_MANUALLY_EMOJI = False
|
|
8
|
+
DEFAULT_EMOJI = '⚫'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_root_dir() -> str:
|
|
12
|
+
"""Priority: env var > persisted file > cwd."""
|
|
13
|
+
env = os.environ.get("SWARM_DEBUG_ROOT")
|
|
14
|
+
if env:
|
|
15
|
+
return os.path.abspath(env)
|
|
16
|
+
|
|
17
|
+
if os.path.exists(ROOT_DIR_FILE):
|
|
18
|
+
with open(ROOT_DIR_FILE, "r") as f:
|
|
19
|
+
saved = f.read().strip()
|
|
20
|
+
if saved:
|
|
21
|
+
return saved
|
|
22
|
+
|
|
23
|
+
return os.getcwd()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def set_root_dir(path: str):
|
|
27
|
+
path = os.path.abspath(path)
|
|
28
|
+
with open(ROOT_DIR_FILE, "w") as f:
|
|
29
|
+
f.write(path)
|