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.
- procler/__init__.py +3 -0
- procler/__main__.py +6 -0
- procler/api/__init__.py +5 -0
- procler/api/app.py +261 -0
- procler/api/deps.py +21 -0
- procler/api/routes/__init__.py +5 -0
- procler/api/routes/config.py +290 -0
- procler/api/routes/groups.py +62 -0
- procler/api/routes/logs.py +43 -0
- procler/api/routes/processes.py +185 -0
- procler/api/routes/recipes.py +69 -0
- procler/api/routes/snippets.py +134 -0
- procler/api/routes/ws.py +459 -0
- procler/cli.py +1478 -0
- procler/config/__init__.py +65 -0
- procler/config/changelog.py +148 -0
- procler/config/loader.py +256 -0
- procler/config/schema.py +315 -0
- procler/core/__init__.py +54 -0
- procler/core/context_base.py +117 -0
- procler/core/context_docker.py +384 -0
- procler/core/context_local.py +287 -0
- procler/core/daemon_detector.py +325 -0
- procler/core/events.py +74 -0
- procler/core/groups.py +419 -0
- procler/core/health.py +280 -0
- procler/core/log_tailer.py +262 -0
- procler/core/process_manager.py +1277 -0
- procler/core/recipes.py +330 -0
- procler/core/snippets.py +231 -0
- procler/core/variable_substitution.py +65 -0
- procler/db.py +96 -0
- procler/logging.py +41 -0
- procler/models.py +130 -0
- procler/py.typed +0 -0
- procler/settings.py +29 -0
- procler/static/assets/AboutView-BwZnsfpW.js +4 -0
- procler/static/assets/AboutView-UHbxWXcS.css +1 -0
- procler/static/assets/Code-HTS-H1S6.js +74 -0
- procler/static/assets/ConfigView-CGJcmp9G.css +1 -0
- procler/static/assets/ConfigView-aVtbRDf8.js +1 -0
- procler/static/assets/DashboardView-C5jw9Nsd.css +1 -0
- procler/static/assets/DashboardView-Dab7Cu9v.js +1 -0
- procler/static/assets/DataTable-z39TOAa4.js +746 -0
- procler/static/assets/DescriptionsItem-B2E8YbqJ.js +74 -0
- procler/static/assets/Divider-Dk-6aD2Y.js +42 -0
- procler/static/assets/Empty-MuygEHZM.js +24 -0
- procler/static/assets/Grid-CZ9QVKAT.js +1 -0
- procler/static/assets/GroupsView-BALG7i1X.js +1 -0
- procler/static/assets/GroupsView-gXAI1CVC.css +1 -0
- procler/static/assets/Input-e0xaxoWE.js +259 -0
- procler/static/assets/PhArrowsClockwise.vue-DqDg31az.js +1 -0
- procler/static/assets/PhCheckCircle.vue-Fwj9sh9m.js +1 -0
- procler/static/assets/PhEye.vue-JcPHciC2.js +1 -0
- procler/static/assets/PhPlay.vue-CZm7Gy3u.js +1 -0
- procler/static/assets/PhPlus.vue-yTWqKlSh.js +1 -0
- procler/static/assets/PhStop.vue-DxsqwIki.js +1 -0
- procler/static/assets/PhTrash.vue-DcqQbN1_.js +125 -0
- procler/static/assets/PhXCircle.vue-BXWmrabV.js +1 -0
- procler/static/assets/ProcessDetailView-DDbtIWq9.css +1 -0
- procler/static/assets/ProcessDetailView-DPtdNV-q.js +1 -0
- procler/static/assets/ProcessesView-B3a6Umur.js +1 -0
- procler/static/assets/ProcessesView-goLmghbJ.css +1 -0
- procler/static/assets/RecipesView-D2VxdneD.js +166 -0
- procler/static/assets/RecipesView-DXnFDCK4.css +1 -0
- procler/static/assets/Select-BBR17AHq.js +317 -0
- procler/static/assets/SnippetsView-B3a9q3AI.css +1 -0
- procler/static/assets/SnippetsView-DBCB2yGq.js +1 -0
- procler/static/assets/Spin-BXTjvFUk.js +90 -0
- procler/static/assets/Tag-Bh_qV63A.js +71 -0
- procler/static/assets/changelog-KkTT4H9-.js +1 -0
- procler/static/assets/groups-Zu-_v8ey.js +1 -0
- procler/static/assets/index-BsN-YMXq.css +1 -0
- procler/static/assets/index-BzW1XhyH.js +1282 -0
- procler/static/assets/procler-DOrSB1Vj.js +1 -0
- procler/static/assets/recipes-1w5SseGb.js +1 -0
- procler/static/index.html +17 -0
- procler/static/procler.png +0 -0
- procler-0.2.0.dist-info/METADATA +545 -0
- procler-0.2.0.dist-info/RECORD +83 -0
- procler-0.2.0.dist-info/WHEEL +4 -0
- procler-0.2.0.dist-info/entry_points.txt +2 -0
- 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
|
+
[](https://pypi.org/project/procler/)
|
|
57
|
+
[](https://pypi.org/project/procler/)
|
|
58
|
+
[](https://github.com/gabu-quest/procler/actions/workflows/ci.yml)
|
|
59
|
+
[](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,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.
|