procler 0.2.0__py3-none-any.whl

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 (83) hide show
  1. procler/__init__.py +3 -0
  2. procler/__main__.py +6 -0
  3. procler/api/__init__.py +5 -0
  4. procler/api/app.py +261 -0
  5. procler/api/deps.py +21 -0
  6. procler/api/routes/__init__.py +5 -0
  7. procler/api/routes/config.py +290 -0
  8. procler/api/routes/groups.py +62 -0
  9. procler/api/routes/logs.py +43 -0
  10. procler/api/routes/processes.py +185 -0
  11. procler/api/routes/recipes.py +69 -0
  12. procler/api/routes/snippets.py +134 -0
  13. procler/api/routes/ws.py +459 -0
  14. procler/cli.py +1478 -0
  15. procler/config/__init__.py +65 -0
  16. procler/config/changelog.py +148 -0
  17. procler/config/loader.py +256 -0
  18. procler/config/schema.py +315 -0
  19. procler/core/__init__.py +54 -0
  20. procler/core/context_base.py +117 -0
  21. procler/core/context_docker.py +384 -0
  22. procler/core/context_local.py +287 -0
  23. procler/core/daemon_detector.py +325 -0
  24. procler/core/events.py +74 -0
  25. procler/core/groups.py +419 -0
  26. procler/core/health.py +280 -0
  27. procler/core/log_tailer.py +262 -0
  28. procler/core/process_manager.py +1277 -0
  29. procler/core/recipes.py +330 -0
  30. procler/core/snippets.py +231 -0
  31. procler/core/variable_substitution.py +65 -0
  32. procler/db.py +96 -0
  33. procler/logging.py +41 -0
  34. procler/models.py +130 -0
  35. procler/py.typed +0 -0
  36. procler/settings.py +29 -0
  37. procler/static/assets/AboutView-BwZnsfpW.js +4 -0
  38. procler/static/assets/AboutView-UHbxWXcS.css +1 -0
  39. procler/static/assets/Code-HTS-H1S6.js +74 -0
  40. procler/static/assets/ConfigView-CGJcmp9G.css +1 -0
  41. procler/static/assets/ConfigView-aVtbRDf8.js +1 -0
  42. procler/static/assets/DashboardView-C5jw9Nsd.css +1 -0
  43. procler/static/assets/DashboardView-Dab7Cu9v.js +1 -0
  44. procler/static/assets/DataTable-z39TOAa4.js +746 -0
  45. procler/static/assets/DescriptionsItem-B2E8YbqJ.js +74 -0
  46. procler/static/assets/Divider-Dk-6aD2Y.js +42 -0
  47. procler/static/assets/Empty-MuygEHZM.js +24 -0
  48. procler/static/assets/Grid-CZ9QVKAT.js +1 -0
  49. procler/static/assets/GroupsView-BALG7i1X.js +1 -0
  50. procler/static/assets/GroupsView-gXAI1CVC.css +1 -0
  51. procler/static/assets/Input-e0xaxoWE.js +259 -0
  52. procler/static/assets/PhArrowsClockwise.vue-DqDg31az.js +1 -0
  53. procler/static/assets/PhCheckCircle.vue-Fwj9sh9m.js +1 -0
  54. procler/static/assets/PhEye.vue-JcPHciC2.js +1 -0
  55. procler/static/assets/PhPlay.vue-CZm7Gy3u.js +1 -0
  56. procler/static/assets/PhPlus.vue-yTWqKlSh.js +1 -0
  57. procler/static/assets/PhStop.vue-DxsqwIki.js +1 -0
  58. procler/static/assets/PhTrash.vue-DcqQbN1_.js +125 -0
  59. procler/static/assets/PhXCircle.vue-BXWmrabV.js +1 -0
  60. procler/static/assets/ProcessDetailView-DDbtIWq9.css +1 -0
  61. procler/static/assets/ProcessDetailView-DPtdNV-q.js +1 -0
  62. procler/static/assets/ProcessesView-B3a6Umur.js +1 -0
  63. procler/static/assets/ProcessesView-goLmghbJ.css +1 -0
  64. procler/static/assets/RecipesView-D2VxdneD.js +166 -0
  65. procler/static/assets/RecipesView-DXnFDCK4.css +1 -0
  66. procler/static/assets/Select-BBR17AHq.js +317 -0
  67. procler/static/assets/SnippetsView-B3a9q3AI.css +1 -0
  68. procler/static/assets/SnippetsView-DBCB2yGq.js +1 -0
  69. procler/static/assets/Spin-BXTjvFUk.js +90 -0
  70. procler/static/assets/Tag-Bh_qV63A.js +71 -0
  71. procler/static/assets/changelog-KkTT4H9-.js +1 -0
  72. procler/static/assets/groups-Zu-_v8ey.js +1 -0
  73. procler/static/assets/index-BsN-YMXq.css +1 -0
  74. procler/static/assets/index-BzW1XhyH.js +1282 -0
  75. procler/static/assets/procler-DOrSB1Vj.js +1 -0
  76. procler/static/assets/recipes-1w5SseGb.js +1 -0
  77. procler/static/index.html +17 -0
  78. procler/static/procler.png +0 -0
  79. procler-0.2.0.dist-info/METADATA +545 -0
  80. procler-0.2.0.dist-info/RECORD +83 -0
  81. procler-0.2.0.dist-info/WHEEL +4 -0
  82. procler-0.2.0.dist-info/entry_points.txt +2 -0
  83. procler-0.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1 @@
1
+ const o="/procler.png";export{o as _};
@@ -0,0 +1 @@
1
+ import{M as T,r as s,p as w}from"./index-BzW1XhyH.js";const y="procler:recipe-last-run";function _(){try{const a=localStorage.getItem(y);return a?JSON.parse(a):{}}catch{return{}}}function L(a){try{localStorage.setItem(y,JSON.stringify(a))}catch{}}const j=T("recipes",()=>{const a=s([]),f=s(null),o=s(null),r=s(!1),t=s(null),i=s(null),c=s(_()),R=w(()=>a.value.length);async function d(){r.value=!0,t.value=null;try{const n=await(await fetch("/api/recipes")).json();n.success?a.value=n.data.recipes:t.value=n.error}catch(e){t.value=String(e)}finally{r.value=!1}}async function S(e){r.value=!0,t.value=null;try{const u=await(await fetch(`/api/recipes/${e}`)).json();u.success?f.value=u.data.recipe:t.value=u.error}catch(n){t.value=String(n)}finally{r.value=!1}}async function v(e,n=!1,u){i.value=e,t.value=null;try{const l=await(await fetch(`/api/recipes/${e}/run`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dry_run:n,continue_on_error:u})})).json();return l.success?(o.value=l.data,n||(c.value={...c.value,[e]:Date.now()},L(c.value))):t.value=l.error,l}catch(p){return t.value=String(p),{success:!1,error:String(p)}}finally{i.value=null}}async function g(e){return v(e,!0)}function h(){o.value=null}function m(e){return c.value[e]??null}return{recipes:a,currentRecipe:f,lastRunResult:o,loading:r,error:t,runningRecipe:i,recipeCount:R,lastRunTimestamps:c,fetchRecipes:d,fetchRecipe:S,runRecipe:v,dryRunRecipe:g,clearLastResult:h,getLastRunTime:m}});export{j as u};
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/png" href="/procler.png" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Procler - Process Manager</title>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
+ <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=Inter:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet" />
11
+ <script type="module" crossorigin src="/assets/index-BzW1XhyH.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-BsN-YMXq.css">
13
+ </head>
14
+ <body>
15
+ <div id="app"></div>
16
+ </body>
17
+ </html>
Binary file
@@ -0,0 +1,545 @@
1
+ Metadata-Version: 2.4
2
+ Name: procler
3
+ Version: 0.2.0
4
+ Summary: LLM-first process manager for developers
5
+ Project-URL: Homepage, https://github.com/gabu-quest/procler
6
+ Project-URL: Repository, https://github.com/gabu-quest/procler
7
+ Project-URL: Documentation, https://github.com/gabu-quest/procler#readme
8
+ Project-URL: Issues, https://github.com/gabu-quest/procler/issues
9
+ Project-URL: Changelog, https://github.com/gabu-quest/procler/blob/main/CHANGELOG.md
10
+ Author: gabu-quest
11
+ License: MIT
12
+ License-File: LICENSE
13
+ Keywords: ai-assistant,claude,cli,devtools,docker,json-api,llm,process-manager
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Environment :: Console
16
+ Classifier: Environment :: Web Environment
17
+ Classifier: Framework :: FastAPI
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: License :: OSI Approved :: MIT License
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Software Development :: Build Tools
25
+ Classifier: Topic :: System :: Monitoring
26
+ Classifier: Topic :: Utilities
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.12
29
+ Requires-Dist: click>=8.1.0
30
+ Requires-Dist: docker>=7.0.0
31
+ Requires-Dist: fastapi>=0.109.0
32
+ Requires-Dist: loguru>=0.7.3
33
+ Requires-Dist: pydantic>=2.0.0
34
+ Requires-Dist: pyyaml>=6.0.0
35
+ Requires-Dist: sqler>=0.1.0
36
+ Requires-Dist: uvicorn[standard]>=0.27.0
37
+ Requires-Dist: websockets>=12.0
38
+ Provides-Extra: dev
39
+ Requires-Dist: build>=1.0.0; extra == 'dev'
40
+ Requires-Dist: httpx>=0.26.0; extra == 'dev'
41
+ Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
42
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
43
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
44
+ Requires-Dist: ruff>=0.2.0; extra == 'dev'
45
+ Requires-Dist: twine>=5.0.0; extra == 'dev'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # Procler
49
+
50
+ [日本語](README.ja.md)
51
+
52
+ <p align="center">
53
+ <img src="procler.png" alt="Procler logo" width="160" height="160" />
54
+ </p>
55
+
56
+ [![PyPI version](https://img.shields.io/pypi/v/procler.svg)](https://pypi.org/project/procler/)
57
+ [![Python versions](https://img.shields.io/pypi/pyversions/procler.svg)](https://pypi.org/project/procler/)
58
+ [![CI](https://github.com/gabu-quest/procler/actions/workflows/ci.yml/badge.svg)](https://github.com/gabu-quest/procler/actions/workflows/ci.yml)
59
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
60
+
61
+ **A process manager where Claude Code is a first-class citizen.**
62
+
63
+ Procler gives developers (and their AI coding assistants) a single pane of glass for managing the chaos of modern development environments - where processes span local shells, Docker containers, and various execution contexts.
64
+
65
+ ## Features
66
+
67
+ - **LLM-First CLI** - JSON-native commands designed for Claude Code integration
68
+ - **Web Dashboard** - Vue 3 dashboard with Cyberpunk design system, real-time updates
69
+ - **Dual Interface Parity** - CLI and Web UI share the same ProcessManager core
70
+ - **Context Abstraction** - Manage local processes and Docker containers uniformly
71
+ - **Groups & Recipes** - Orchestrate multi-process workflows with dependencies
72
+ - **Health Checks** - Monitor process health with configurable checks
73
+ - **Snippets** - Save and reuse common commands with tagging
74
+ - **Real-time Updates** - WebSocket support for live status and log streaming
75
+ - **Config Variables** - Define `vars` in config.yaml and reference with `${VAR}`
76
+
77
+ ## Installation
78
+
79
+ ```bash
80
+ pip install procler
81
+ ```
82
+
83
+ Or install from source:
84
+
85
+ ```bash
86
+ git clone https://github.com/gabu-quest/procler.git
87
+ cd procler
88
+ uv pip install -e .[dev]
89
+ ```
90
+
91
+ > **Note:** Frontend is pre-built and included. No separate build step needed!
92
+
93
+ ## Quick Start
94
+
95
+ ### Initialize Configuration
96
+
97
+ ```bash
98
+ # Create .procler/ config directory
99
+ procler config init
100
+
101
+ # Validate your config
102
+ procler config validate
103
+
104
+ # Get a plain-language explanation of your config
105
+ procler config explain
106
+ ```
107
+
108
+ ### Process Management
109
+
110
+ ```bash
111
+ # Define a process
112
+ procler define --name my-api --command "uvicorn main:app --port 8000"
113
+
114
+ # Start it
115
+ procler start my-api
116
+
117
+ # Check status (JSON output)
118
+ procler status my-api
119
+
120
+ # View logs
121
+ procler logs my-api --tail 50 --since 5m
122
+
123
+ # Stop it
124
+ procler stop my-api
125
+
126
+ # Restart (with optional log clearing)
127
+ procler restart my-api --clear-logs
128
+ ```
129
+
130
+ ### Docker Processes
131
+
132
+ ```bash
133
+ # Define a process that runs in a Docker container
134
+ procler define \
135
+ --name db-migrate \
136
+ --command "alembic upgrade head" \
137
+ --context docker \
138
+ --container api-container
139
+
140
+ # Execute arbitrary command in container
141
+ procler exec "pip list" --context docker --container api-container
142
+ ```
143
+
144
+ ### Groups & Recipes
145
+
146
+ ```bash
147
+ # Start all processes in a group (respects order and dependencies)
148
+ procler group start backend
149
+
150
+ # Stop in reverse order
151
+ procler group stop backend
152
+
153
+ # Preview a recipe before running
154
+ procler recipe run deploy --dry-run
155
+
156
+ # Execute the recipe
157
+ procler recipe run deploy
158
+ ```
159
+
160
+ ### Variable Substitution
161
+
162
+ Define vars in `.procler/config.yaml` and reference them in commands and container names:
163
+
164
+ ```yaml
165
+ vars:
166
+ SIM_CONTAINER: my-sim-container
167
+ SIM_USER: "1000"
168
+ SIM_WORKDIR: /opt/sim
169
+
170
+ processes:
171
+ simulator:
172
+ command: "${SIM_WORKDIR}/bin/simulator"
173
+ context: docker
174
+ container: "${SIM_CONTAINER}"
175
+ ```
176
+
177
+ ### Snippets (Reusable Commands)
178
+
179
+ ```bash
180
+ # Save a snippet
181
+ procler snippet save \
182
+ --name rebuild-api \
183
+ --command "docker compose build api" \
184
+ --tags docker,build
185
+
186
+ # List snippets (with optional tag filter)
187
+ procler snippet list --tag docker
188
+
189
+ # Run a snippet
190
+ procler snippet run rebuild-api
191
+ ```
192
+
193
+ ### Web Server
194
+
195
+ ```bash
196
+ # Start the API server
197
+ procler serve --host 0.0.0.0 --port 8000
198
+
199
+ # With hot reload for development
200
+ procler serve --reload
201
+ ```
202
+
203
+ ## CLI Reference
204
+
205
+ All CLI commands return structured JSON for easy parsing by scripts and LLMs.
206
+
207
+ ### Discovery & Config Commands
208
+
209
+ | Command | Description |
210
+ |---------|-------------|
211
+ | `procler capabilities` | Returns JSON schema of all commands (LLM discovery) |
212
+ | `procler help-llm` | Output comprehensive LLM-focused usage instructions |
213
+ | `procler config init [--force]` | Initialize `.procler/` config directory with template |
214
+ | `procler config validate` | Validate config.yaml syntax and all references |
215
+ | `procler config path` | Show the resolved config directory path |
216
+ | `procler config explain` | Explain config in plain language (LLM-friendly) |
217
+
218
+ ### Process Commands
219
+
220
+ | Command | Description |
221
+ |---------|-------------|
222
+ | `procler define --name NAME --command CMD [options]` | Define a new process |
223
+ | `procler start NAME` | Start a process (idempotent) |
224
+ | `procler stop NAME` | Stop a process (idempotent) |
225
+ | `procler restart NAME [--clear-logs]` | Restart a process |
226
+ | `procler status [NAME]` | Show status (all or single process) |
227
+ | `procler list [--resolve]` | List all process definitions |
228
+ | `procler remove NAME` | Remove a process definition |
229
+ | `procler logs NAME [--tail N] [--since TIME] [-f]` | Get/follow logs |
230
+ | `procler exec "CMD" [--context TYPE] [--container NAME]` | Execute one-off command |
231
+
232
+ #### Define Options
233
+
234
+ | Option | Description |
235
+ |--------|-------------|
236
+ | `--name` | Process name (required) |
237
+ | `--command` | Command to execute (required) |
238
+ | `--context` | `local` or `docker` (default: local) |
239
+ | `--container` | Docker container name (required for docker) |
240
+ | `--cwd` | Working directory |
241
+ | `--display-name` | Human-friendly name |
242
+ | `--tags` | Comma-separated tags |
243
+ | `--daemon-mode` | Enable daemon mode (process forks) |
244
+ | `--daemon-pattern` | Process name pattern to match daemon |
245
+ | `--daemon-pidfile` | Path to daemon pidfile |
246
+ | `--force` | Overwrite existing definition |
247
+
248
+ ### Group Commands
249
+
250
+ | Command | Description |
251
+ |---------|-------------|
252
+ | `procler group list` | List all groups defined in config |
253
+ | `procler group start NAME` | Start all processes in order |
254
+ | `procler group stop NAME` | Stop all processes in reverse/custom order |
255
+ | `procler group status NAME` | Get status of all processes in group |
256
+
257
+ ### Recipe Commands
258
+
259
+ | Command | Description |
260
+ |---------|-------------|
261
+ | `procler recipe list` | List all recipes defined in config |
262
+ | `procler recipe show NAME` | Show recipe details and steps |
263
+ | `procler recipe run NAME [--dry-run] [--continue-on-error]` | Execute a recipe |
264
+
265
+ ### Snippet Commands
266
+
267
+ | Command | Description |
268
+ |---------|-------------|
269
+ | `procler snippet list [--tag TAG]` | List all snippets |
270
+ | `procler snippet show NAME` | Show snippet details |
271
+ | `procler snippet save --name NAME --command CMD [options]` | Save a new snippet |
272
+ | `procler snippet run NAME` | Run a saved snippet |
273
+ | `procler snippet remove NAME` | Remove a snippet |
274
+
275
+ ### Server Commands
276
+
277
+ | Command | Description |
278
+ |---------|-------------|
279
+ | `procler serve [--host HOST] [--port PORT] [--reload]` | Start the web server |
280
+
281
+ ## Configuration
282
+
283
+ Procler uses a per-project `.procler/` directory:
284
+
285
+ ```
286
+ .procler/
287
+ ├── config.yaml # Definitions (commit to git)
288
+ ├── changelog.log # Audit trail (commit to git)
289
+ └── state.db # Runtime state (auto-gitignored)
290
+ ```
291
+
292
+ **Discovery order:** `$PROCLER_CONFIG_DIR` → `.procler.env` → `.procler/` → git root → `~/.procler/`
293
+
294
+ ### Example Configuration
295
+
296
+ ```yaml
297
+ version: 1
298
+
299
+ vars:
300
+ API_PORT: "8000"
301
+ DB_CONTAINER: postgres-dev
302
+
303
+ processes:
304
+ api:
305
+ command: uvicorn main:app --reload --port ${API_PORT}
306
+ context: local
307
+ cwd: /path/to/project
308
+ tags: [backend, api]
309
+ description: "API server"
310
+ healthcheck:
311
+ test: "curl -f http://localhost:${API_PORT}/health"
312
+ interval: 10s
313
+ timeout: 5s
314
+ retries: 3
315
+ start_period: 30s
316
+
317
+ worker:
318
+ command: celery worker -A tasks
319
+ context: local
320
+ depends_on:
321
+ - redis
322
+ - name: api
323
+ condition: healthy # Wait for health check
324
+
325
+ db-migrate:
326
+ command: alembic upgrade head
327
+ context: docker
328
+ container: ${DB_CONTAINER}
329
+
330
+ groups:
331
+ backend:
332
+ description: "Full backend stack"
333
+ processes: [redis, api, worker]
334
+ stop_order: [worker, api, redis] # Optional custom order
335
+
336
+ recipes:
337
+ deploy:
338
+ description: "Graceful deployment"
339
+ on_error: stop # or continue
340
+ steps:
341
+ - stop: worker
342
+ - stop: api
343
+ - wait: 2s
344
+ - exec: "alembic upgrade head"
345
+ context: docker
346
+ container: ${DB_CONTAINER}
347
+ - start: api
348
+ - start: worker
349
+
350
+ snippets:
351
+ rebuild:
352
+ command: docker compose build
353
+ description: "Rebuild containers"
354
+ tags: [docker]
355
+ ```
356
+
357
+ ## CLI Output Format
358
+
359
+ All CLI commands return structured JSON:
360
+
361
+ ### Success Response
362
+
363
+ ```json
364
+ {
365
+ "success": true,
366
+ "data": {
367
+ "processes": [
368
+ {
369
+ "name": "my-api",
370
+ "status": "running",
371
+ "pid": 12345,
372
+ "uptime_seconds": 3600
373
+ }
374
+ ]
375
+ }
376
+ }
377
+ ```
378
+
379
+ ### Error Response
380
+
381
+ ```json
382
+ {
383
+ "success": false,
384
+ "error": "Container 'db-postgres' not found",
385
+ "error_code": "container_not_found",
386
+ "suggestion": "Run 'docker ps -a' to list available containers"
387
+ }
388
+ ```
389
+
390
+ ## REST API
391
+
392
+ Base URL: `http://localhost:8000/api`
393
+
394
+ | Endpoint | Method | Description |
395
+ |----------|--------|-------------|
396
+ | `/api/processes` | GET | List all processes |
397
+ | `/api/processes` | POST | Create process |
398
+ | `/api/processes/{name}` | GET | Get process |
399
+ | `/api/processes/{name}` | DELETE | Remove process |
400
+ | `/api/processes/{name}/start` | POST | Start process |
401
+ | `/api/processes/{name}/stop` | POST | Stop process |
402
+ | `/api/processes/{name}/restart` | POST | Restart process |
403
+ | `/api/logs/{name}` | GET | Get logs (?tail=100&since=5m) |
404
+ | `/api/groups` | GET | List all groups |
405
+ | `/api/groups/{name}/start` | POST | Start group |
406
+ | `/api/groups/{name}/stop` | POST | Stop group |
407
+ | `/api/groups/{name}/status` | GET | Group status |
408
+ | `/api/recipes` | GET | List all recipes |
409
+ | `/api/recipes/{name}/run` | POST | Run recipe |
410
+ | `/api/snippets` | GET | List snippets (?tag=filter) |
411
+ | `/api/snippets` | POST | Create snippet |
412
+ | `/api/snippets/{name}` | GET | Get snippet |
413
+ | `/api/snippets/{name}` | DELETE | Remove snippet |
414
+ | `/api/snippets/{name}/run` | POST | Run snippet |
415
+ | `/api/config` | GET | Config status |
416
+ | `/api/config/reload` | POST | Reload config |
417
+ | `/api/health` | GET | Health check |
418
+
419
+ ## WebSocket
420
+
421
+ Connect to `ws://localhost:8000/api/ws` for real-time updates.
422
+
423
+ ```javascript
424
+ // Subscribe to logs for a process
425
+ ws.send(JSON.stringify({action: "subscribe_logs", process_id: 1}));
426
+
427
+ // Subscribe to status updates (all processes)
428
+ ws.send(JSON.stringify({action: "subscribe_status"}));
429
+
430
+ // Receive updates
431
+ // {"type": "log", "process_id": 1, "data": {"line": "...", "stream": "stdout"}}
432
+ // {"type": "status", "process_id": 1, "data": {"status": "running", "pid": 123}}
433
+ ```
434
+
435
+ ## Tech Stack
436
+
437
+ | Layer | Technology |
438
+ |-------|------------|
439
+ | Backend | Python 3.12+, FastAPI |
440
+ | Database | SQLite via [sqler](https://pypi.org/project/sqler/) |
441
+ | Frontend | Vue 3, Vite, Pinia, Naive UI |
442
+ | CLI | Click |
443
+ | Docker | docker-py SDK |
444
+ | Real-time | WebSockets |
445
+
446
+ ## Development
447
+
448
+ ```bash
449
+ # Install dev dependencies
450
+ uv pip install -e .[dev]
451
+
452
+ # Run tests (154 tests)
453
+ uv run pytest -v
454
+
455
+ # Run CLI in development
456
+ uv run procler --help
457
+
458
+ # Run server with hot reload
459
+ uv run procler serve --reload
460
+
461
+ # Rebuild frontend (only if modifying Vue code)
462
+ bash scripts/build_frontend.sh
463
+ ```
464
+
465
+ ### Pre-commit Hooks
466
+
467
+ This project uses pre-commit hooks for code quality. Install them with:
468
+
469
+ ```bash
470
+ pre-commit install
471
+ ```
472
+
473
+ The hooks run ruff for linting and formatting on every commit.
474
+
475
+ ## Web Dashboard
476
+
477
+ The Vue 3 frontend provides a visual interface for managing processes and snippets:
478
+
479
+ - **Dashboard** - Overview of all processes with real-time status
480
+ - **Process List** - View all defined processes with start/stop/restart controls, per-action loading states
481
+ - **Process Detail** - Live log streaming via WebSocket with search/filter and stream filtering
482
+ - **Groups** - Card-based view with one-click start/stop all
483
+ - **Recipes** - Step preview, dry-run, execution progress
484
+ - **Snippets** - Save, manage, and run reusable commands with confirmations
485
+ - **Config** - Status, stats, variable display, changelog viewer
486
+
487
+ ### Keyboard Shortcuts
488
+
489
+ Press `?` to view all shortcuts. Quick navigation:
490
+ - `g d` - Dashboard
491
+ - `g p` - Processes
492
+ - `g g` - Groups
493
+ - `g r` - Recipes
494
+ - `g s` - Snippets
495
+ - `g c` - Config
496
+ - `g a` - About
497
+
498
+ ### UX Features
499
+
500
+ - **Connection Status** - WebSocket indicator in header shows connected/connecting/error states
501
+ - **Toast Notifications** - Automatic notifications when process status changes
502
+ - **Log Search** - Filter logs by text (with match highlighting) or stream (stdout/stderr)
503
+ - **Breadcrumbs** - Navigation context on detail pages
504
+ - **Confirmations** - Destructive actions require confirmation
505
+
506
+ ### Running the Dashboard
507
+
508
+ ```bash
509
+ # Start the server (serves both API and web UI)
510
+ uv run procler serve --port 8000
511
+ ```
512
+
513
+ Open http://localhost:8000 to access the dashboard.
514
+
515
+ > **Frontend Development:** For Vue development, run `cd frontend && npm run dev` (dev server on port 5173) and rebuild with `bash scripts/build_frontend.sh`
516
+
517
+ ## Claude Code Integration
518
+
519
+ Procler is designed for seamless AI assistant integration:
520
+
521
+ ```
522
+ Human: "My auth-api seems slow, check its recent logs and restart it if there are errors"
523
+
524
+ Claude Code:
525
+ 1. procler logs auth-api --tail 100
526
+ 2. [Analyzes JSON log output]
527
+ 3. procler restart auth-api
528
+ 4. procler status auth-api
529
+ 5. Reports back to human
530
+ ```
531
+
532
+ ## Environment Variables
533
+
534
+ | Variable | Default | Description |
535
+ |----------|---------|-------------|
536
+ | `PROCLER_LOG_LEVEL` | `INFO` | Log level (DEBUG, INFO, WARNING, ERROR) |
537
+ | `PROCLER_LOG_FILE` | - | Log file path (auto-rotates) |
538
+ | `PROCLER_CONFIG_DIR` | `.procler/` | Config directory |
539
+ | `PROCLER_DB_PATH` | `.procler/state.db` | Database path |
540
+ | `PROCLER_CORS_ORIGINS` | `localhost` | Comma-separated allowed origins |
541
+ | `PROCLER_DEBUG` | - | Enable detailed error messages |
542
+
543
+ ## License
544
+
545
+ MIT
@@ -0,0 +1,83 @@
1
+ procler/__init__.py,sha256=Ma1t1twEjlH545yxz38m8zlHdElmvM-oYzhsHQqk-Lo,81
2
+ procler/__main__.py,sha256=pU-IhqkyV6dSnVS07_iqxYuNQhBvHIVndQ0ctg3cAYs,101
3
+ procler/cli.py,sha256=Av7IeNo8LtW01179BVAIkSnPt36JFuBy4W3AgGEWslc,47251
4
+ procler/db.py,sha256=tYxJHM6ddfz9rXC8tVtpCATSVplB2F7__uftIS3BPs4,2741
5
+ procler/logging.py,sha256=Iy24kY0Cf2Sw25XK1qzBdKaifSQ0E2gy2ebnJXF1So4,1005
6
+ procler/models.py,sha256=JayAeKsnJk5idx7cDhtdu8L5HsensEydy4D2CGaLseI,3189
7
+ procler/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ procler/settings.py,sha256=D6EsqN69ufY_-r8T9KyxoqaCZSp-iUlXvDsN00cqk7o,791
9
+ procler/api/__init__.py,sha256=Fjy8MxLZukGUOMyKhTaSehcCQ5rL05dsGIj2At4MaQE,106
10
+ procler/api/app.py,sha256=uaiIq5r1iriGLtWlzcwanRHe2sL5ExdHFBGp-JN3TLM,8966
11
+ procler/api/deps.py,sha256=ZXSYYssny91rLZHjFn-3WVSSfKE5hOhK4bqUvrPm6RA,531
12
+ procler/api/routes/__init__.py,sha256=fnVj-QBCpVZPEmD7Uoi2lSBV9VuNgsGaz6clGm1vZyU,177
13
+ procler/api/routes/config.py,sha256=xH9R6rrDf4FA9vRmYw6wR3TvcC77kaCYoiu1hLLd42o,9374
14
+ procler/api/routes/groups.py,sha256=6HYS4cSVTrpExxu-uuo425m1PkJTxUKMq5Y8EdLUY7c,1653
15
+ procler/api/routes/logs.py,sha256=A5mEWZqk46mrq9VQ4sCjdT0i-e04xV_66i5zQfOjZ5c,1156
16
+ procler/api/routes/processes.py,sha256=pp5Y60WBKb5lSBmfJQAQ88hmtxT56qT6AMM97BnCdgw,5237
17
+ procler/api/routes/recipes.py,sha256=O5QKpPhDpRiej-3Ri0n5zFQ0oyMij97MrcYjojozZpQ,1867
18
+ procler/api/routes/snippets.py,sha256=MPqWC56qeJ5gvSwAvw6vyy5OMbg4n41RxdP4nki_T8k,3592
19
+ procler/api/routes/ws.py,sha256=WWwsevrGb-jE58-44K1FUmbkRGXvSDRD8cKbkRtNgNA,19016
20
+ procler/config/__init__.py,sha256=zkvPy9SrvnsxbYt8xZv4E2gRyv9bSA7N2uiytRvCo6Q,1356
21
+ procler/config/changelog.py,sha256=hiAcXNkTVioa-Upk3K_8QXNrOciXvKIstGlB7KO30IU,4246
22
+ procler/config/loader.py,sha256=c67MM6J3gQZyhZnyAFPKhnbGfFxKHPFz37mnLAQbO3o,7590
23
+ procler/config/schema.py,sha256=sdJEPFOJHRVe2RaY4Y_Yzk772yFI_7jP8I-NPG0Tx6M,10496
24
+ procler/core/__init__.py,sha256=At0uc8_e4eOpJxvLP1N43U5tgiuOYGh_P_GPVRdNeD4,1449
25
+ procler/core/context_base.py,sha256=ZZrHCncVTwV13o90CkN1KZvFwug_gGaPjvDqOajSMTc,3066
26
+ procler/core/context_docker.py,sha256=yJUkVggRUKazbbKZ2fCRRFqemNlG08WX8iroQVvCygQ,13532
27
+ procler/core/context_local.py,sha256=ygnoioCwvSCbbWLgL9yXddDnJpwR2HVRJ2d8ikkFM9U,9372
28
+ procler/core/daemon_detector.py,sha256=hSs_iHVW4XJX3fECo6IPDKhDAPohU2_LcD0EaSmzGIE,10980
29
+ procler/core/events.py,sha256=6_fn5pfSKwchy7WviLr9FRNfHbWqcNsGnr7eXvuBIhc,2319
30
+ procler/core/groups.py,sha256=u3AR82BZxWDeS0S8uerAG70eckiwsC5V_dxI3bs6RXk,14006
31
+ procler/core/health.py,sha256=0r4vl-VyQYWYFVQIBBaXHt39niVZYSIIHJMbxoxp3Tg,9302
32
+ procler/core/log_tailer.py,sha256=KV-RVc2AXql5Myc0ni4bQN_5h03Cju-PaPBz1B9iNHE,9503
33
+ procler/core/process_manager.py,sha256=SYBE7pVWN66z_DGl1ws8YtfdXTcPXu846g0MotUbv2Q,48338
34
+ procler/core/recipes.py,sha256=oVqwi1triImGALrqCC15nVEB27C0QNaMIg2m6_JJoAQ,11035
35
+ procler/core/snippets.py,sha256=UzsBLTC8lPPF_FbFEIktY4jh0g7hOlW71ey-d9resk0,7190
36
+ procler/core/variable_substitution.py,sha256=Or2yDJ03lWTB3P3WKYllPhEjtKLX7W43sr4S2El8kZY,1838
37
+ procler/static/index.html,sha256=8IMkkJGUn0OtOPSrLQ_3mJuZUcaLPtu_Gdvu97bdmT0,797
38
+ procler/static/procler.png,sha256=elTrUiNOON4lmVTAcuJXn9ip92mSg_JcZJGRXG_CmrM,786441
39
+ procler/static/assets/AboutView-BwZnsfpW.js,sha256=sE_sq5PhJY9jFFYgzCTtpSm-AX3XOMlxq14mQDw6xrA,17392
40
+ procler/static/assets/AboutView-UHbxWXcS.css,sha256=9Fmv1-JquDu_l_41qSOA_VOGH-t25gVWMSFEWJm2yQ8,3159
41
+ procler/static/assets/Code-HTS-H1S6.js,sha256=-B0fS9YYARD4a2QzK5VVzv7l4ogXZ0t2F7ds7ZfiULs,4911
42
+ procler/static/assets/ConfigView-CGJcmp9G.css,sha256=3Edz0G4W_WN6IqCf49riwxlcMVQWHKOFBA8os6Cw7Wc,2241
43
+ procler/static/assets/ConfigView-aVtbRDf8.js,sha256=GCD0FSvJM29qv76_ZyKXo0QC0zJL9KwPo4UatIECdaU,7189
44
+ procler/static/assets/DashboardView-C5jw9Nsd.css,sha256=NvY0Dada_YAXoHa4lfwydVI-23OJg86BEBuvnwrqdAM,4715
45
+ procler/static/assets/DashboardView-Dab7Cu9v.js,sha256=EtLueTBFCgH5TydEkUgK7ybWnOFxWuGTBErDKF963Zw,7257
46
+ procler/static/assets/DataTable-z39TOAa4.js,sha256=s7cL96XMn5yDPrK-cIGddErQlGEoM-gBLk6iBcFo9dk,117036
47
+ procler/static/assets/DescriptionsItem-B2E8YbqJ.js,sha256=l8OXv5whrDd3rUwno3WOq9ErRKIyTchhJVThCPqQRM0,7948
48
+ procler/static/assets/Divider-Dk-6aD2Y.js,sha256=uqOdz9dk8VITMh2Y2ilxbKkRT-NXv1IH7m7Jy1RuJjI,2503
49
+ procler/static/assets/Empty-MuygEHZM.js,sha256=UqguhspcjKA5_dZ0J400URNtjbZaTtQfOBToONouoGg,13728
50
+ procler/static/assets/Grid-CZ9QVKAT.js,sha256=DOo8VgcgUaeWTkqbXdiKh256_0jmBUE1WofoRDbuCZc,6286
51
+ procler/static/assets/GroupsView-BALG7i1X.js,sha256=bN9lKCvsJGrwB9huEvSNLxragAmGCOmBwOZqMH4alNQ,9902
52
+ procler/static/assets/GroupsView-gXAI1CVC.css,sha256=opTOR4SQBHHvc7OrAQregjK10qMnJf7s61_2Sx8U6aU,2291
53
+ procler/static/assets/Input-e0xaxoWE.js,sha256=-k-7cbzgBLP5yNs8T_h_e--w7i2LlPnNNOPlQnxgGYs,33042
54
+ procler/static/assets/PhArrowsClockwise.vue-DqDg31az.js,sha256=skcVDOBIewjR-OuZ4N_0iIwy20w2iCV-LPZEHspblgE,3855
55
+ procler/static/assets/PhCheckCircle.vue-Fwj9sh9m.js,sha256=qYr7ATiCIBOy49CKfOjRVCTVf9CKCszXCdCR6zWbYkg,2653
56
+ procler/static/assets/PhEye.vue-JcPHciC2.js,sha256=IKiYlPzW3TMUEc2W0h72Up6xUfdVHrTlYhwARt8iV1E,4765
57
+ procler/static/assets/PhPlay.vue-CZm7Gy3u.js,sha256=bD6IezhTUBrjtpEke61flYAcbmYHZtADwxMlx2kzC84,2761
58
+ procler/static/assets/PhPlus.vue-yTWqKlSh.js,sha256=WKHzx__iu-mjwo14N42ieSXlNsYczCX2StlhMytliX4,2107
59
+ procler/static/assets/PhStop.vue-DxsqwIki.js,sha256=RYiHe4lZQ_-6b3rtifLPM6f83qDAHVl833pX_54ULD4,2110
60
+ procler/static/assets/PhTrash.vue-DcqQbN1_.js,sha256=lcXg0kFd2Kpa9Hna1RzX4k0etrp8aOAJx3lQK0sJeRc,40594
61
+ procler/static/assets/PhXCircle.vue-BXWmrabV.js,sha256=LZsSTzi7MsQJeXAkW9nERMyE3NCxdbxCSXRts1kV-ms,3019
62
+ procler/static/assets/ProcessDetailView-DDbtIWq9.css,sha256=6oKlyYrScOvVHI9BLnu73hp7PX2xbD88v7EenZPnos4,2758
63
+ procler/static/assets/ProcessDetailView-DPtdNV-q.js,sha256=njAzi_rnqLDqBp2FmapeYcxazmZdPf9xnuh5KgBAzsg,13341
64
+ procler/static/assets/ProcessesView-B3a6Umur.js,sha256=zB4GlZE7i4Bn20XlDi0HvKsAxENEXJe6wFnVOfUd4SY,12722
65
+ procler/static/assets/ProcessesView-goLmghbJ.css,sha256=dxI3jJkrmMcgK4Hdeq2GwQNm_OkRDQwiug4OfcvWW_0,3259
66
+ procler/static/assets/RecipesView-D2VxdneD.js,sha256=OcYV6f9X-cmi5OgZk1CbFBm3G3NWlueg9yeN1kmlCH0,45432
67
+ procler/static/assets/RecipesView-DXnFDCK4.css,sha256=NXtwl-lgcmAPLj3IdTcgBn1moynx6pk64XwbhGYTOTg,4819
68
+ procler/static/assets/Select-BBR17AHq.js,sha256=f_jpVWFbk1pwsT7FVJESRzvZ-9FmVIOWNG4-AMHKKms,54655
69
+ procler/static/assets/SnippetsView-B3a9q3AI.css,sha256=LJupEX22_ALJ5Unrlwp8n0gyGWMUc7sp5lrIU6pP0xc,387
70
+ procler/static/assets/SnippetsView-DBCB2yGq.js,sha256=u35SpRsHkrLmGM5ZG3U8fOxrh4xP6JhembsoawmnU7k,7211
71
+ procler/static/assets/Spin-BXTjvFUk.js,sha256=_FEOcjrMAjEmEGr8lHggGr0cVOTQ6oJGDMjsdORV374,13119
72
+ procler/static/assets/Tag-Bh_qV63A.js,sha256=oJDspqm-6sGuFppnrzdoQ0vqdDwbN_63ucme063o-KE,9794
73
+ procler/static/assets/changelog-KkTT4H9-.js,sha256=wAO6FMmSz9WkpWzbmkYa98XARB0SdC5bozLLffN8xgA,2144
74
+ procler/static/assets/groups-Zu-_v8ey.js,sha256=NglRdRivRsdaNKmTpy59uSgG7BcOKm-FZbJyL45GFVE,965
75
+ procler/static/assets/index-BsN-YMXq.css,sha256=nqNr_jOrtLP5IXYeKVPW9-XZwpzmpHI_rbWN9PwnIeA,3159
76
+ procler/static/assets/index-BzW1XhyH.js,sha256=Y9fMYfZVnHRn2fJTj1uYQmrHnrcYzmT0KndQqx66URc,595296
77
+ procler/static/assets/procler-DOrSB1Vj.js,sha256=BIaPRlQkKIAsNYhgJkXIOqa5PYe-Fl-6b1Kj0cPRwsg,39
78
+ procler/static/assets/recipes-1w5SseGb.js,sha256=JfpRpjP2AbX3hB47P5n3vJPpX9jnCioxrFccg99PO2s,1499
79
+ procler-0.2.0.dist-info/METADATA,sha256=gVhyoqBZbdz7jlYHEh_LlT1E6dMnTJWy7ti7M5FdYdU,15884
80
+ procler-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
81
+ procler-0.2.0.dist-info/entry_points.txt,sha256=bIIyYJtx7Er-hlPFiL1Se7DHxd-O7ofBhXKCIC86C2A,44
82
+ procler-0.2.0.dist-info/licenses/LICENSE,sha256=SaPvdtwQLl1BA-6s4Exl0M3X7L_6gcnOGzBD86t7dIo,1061
83
+ procler-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ procler = procler.cli:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gabu
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.