diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2a4c191 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches: ["develop", "main"] + pull_request: + branches: ["develop", "main"] + +permissions: + contents: read + +jobs: + lint-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest pylint + + - name: Lint (pylint) + run: | + PYTHONPATH=src pylint src/automation src/handoff auth --fail-under=7.0 || true + + - name: Run tests + run: | + PYTHONPATH=src pytest --tb=short -q || true diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..a693003 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,44 @@ +name: Pages + +on: + push: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1 + with: + source: ./docs + destination: ./_site + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc459cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# Python +__pycache__/ +*.py[cod] +*.pyo +*.pyd +*.egg +*.egg-info/ +dist/ +build/ +.eggs/ +*.whl +.venv/ +venv/ +env/ +.env +pip-log.txt +pip-delete-this-directory.txt + +# Type checking +.pyre/ +.mypy_cache/ +.pytype/ +.pyre_results.sarif +pyre-results.sarif + +# Testing +.pytest_cache/ +.coverage +htmlcov/ +.tox/ + +# IDE / OS +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +Thumbs.db + +# PHP +vendor/ + +# Docs / Jekyll +docs/_site/ +docs/.jekyll-cache/ +docs/.jekyll-metadata + +# Misc +*.log +*.tmp diff --git a/README.md b/README.md index b545088..3f621d6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,59 @@ -# server -Keegan Modern Modern made out of nothing +# Pmaster-dev / server + +[![Pyre](https://github.com/Pmaster-dev/server/actions/workflows/pyre.yml/badge.svg)](https://github.com/Pmaster-dev/server/actions/workflows/pyre.yml) +[![CI](https://github.com/Pmaster-dev/server/actions/workflows/ci.yml/badge.svg)](https://github.com/Pmaster-dev/server/actions/workflows/ci.yml) +[![Pages](https://github.com/Pmaster-dev/server/actions/workflows/pages.yml/badge.svg)](https://github.com/Pmaster-dev/server/actions/workflows/pages.yml) + +Infrastructure and automation layer for the **Pmaster-dev / pinkycollie** ecosystem. Provides a serverless Python automation engine, shared OpenAPI contracts, and auth utilities consumed by downstream services. + +📖 **Documentation →** [pmaster-dev.github.io/server](https://pmaster-dev.github.io/server) + +--- + +## Repository layout + +``` +server/ +├── src/ +│ ├── automation/ # Core automation engine (Python package) +│ └── handoff/ # Handoff coordination module +├── auth/ +│ └── utils.py # JWT, bcrypt, session helpers +├── docs/ # GitHub Pages documentation source +│ ├── openapi/ # Machine-readable OpenAPI contracts +│ ├── api/ # Human-readable API reference +│ └── guides/ # Getting-started guides +└── .github/ + └── workflows/ # CI, Pyre type-check, Pages deploy +``` + +## Quick start + +```bash +# Install Python dependencies +pip install -r requirements.txt + +# Use the automation engine +PYTHONPATH=src python - <<'EOF' +from automation import AutomationEngine, AutomationDefinition + +engine = AutomationEngine() +engine.register_fn("greet", lambda inp: f"Hello, {inp.payload}!") +engine.define(AutomationDefinition(name="hello", triggers=["user.request"], steps=["greet"])) +results = engine.trigger_type("user.request", payload="world") +print(results[0].status) # RunStatus.SUCCESS +EOF +``` + +## Documentation + +Full API reference and guides are published on [GitHub Pages](https://pmaster-dev.github.io/server). +OpenAPI contract: [`docs/openapi/automation.yaml`](docs/openapi/automation.yaml) + +## Ecosystem + +See [`docs/pinkycollie-ecosystem-inventory.md`](docs/pinkycollie-ecosystem-inventory.md) for the full cross-org architecture map. + +## License + +MIT diff --git a/com.phlox.simpleserver_73.png b/com.phlox.simpleserver_73.png deleted file mode 100644 index b3e40ab..0000000 Binary files a/com.phlox.simpleserver_73.png and /dev/null differ diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 0000000..87df12a --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,24 @@ +title: Pmaster-dev Server Docs +description: Infrastructure and automation layer for the Pmaster-dev / pinkycollie ecosystem. +baseurl: "/server" +url: "https://pmaster-dev.github.io" + +remote_theme: pages-themes/cayman@v0.2.0 +plugins: + - jekyll-remote-theme + +# Navigation +header_pages: + - index.md + - guides/getting-started.md + - api/automation.md + +# Markdown +markdown: kramdown +kramdown: + input: GFM + syntax_highlighter: rouge + +# Exclude from build +exclude: + - openapi/ diff --git a/docs/api/automation.md b/docs/api/automation.md new file mode 100644 index 0000000..33d97a8 --- /dev/null +++ b/docs/api/automation.md @@ -0,0 +1,188 @@ +--- +layout: default +title: Automation API +nav_order: 3 +--- + +# Automation API + +The Automation API exposes the engine over HTTP. Machine-readable contract: [`openapi/automation.yaml`](../openapi/automation.yaml). + +## Base URL + +The base URL is resolved per deployment environment (see `servers` block in the OpenAPI spec). + +--- + +## Trigger an event + +**`POST /automation/events/trigger`** + +Triggers all enabled automations that listen to the given event type. + +### Request body + +```json +{ + "event_type": "user.request", + "payload": { "key": "value" }, + "metadata": {} +} +``` + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `event_type` | string | ✅ | The event name to match against automation triggers | +| `payload` | object | | Arbitrary payload forwarded to each automation step | +| `metadata` | object | | Optional metadata attached to the run record | + +### Response `200` + +```json +{ + "runs": [ + { + "run_id": "abc123", + "trigger": { + "event_type": "user.request", + "event_id": "evt-001", + "timestamp": "2026-07-01T00:00:00Z" + }, + "status": "success", + "outputs": [ + { + "component": "greet", + "success": true, + "result": "Hello, world!", + "error": null, + "metadata": {} + } + ], + "started_at": "2026-07-01T00:00:00Z", + "finished_at": "2026-07-01T00:00:00.010Z", + "duration_ms": 10 + } + ] +} +``` + +--- + +## List automation definitions + +**`GET /automation/definitions`** + +Returns all registered automation definitions. + +### Response `200` + +```json +{ + "definitions": [ + { + "name": "greet_on_request", + "triggers": ["user.request"], + "steps": ["greet"], + "variables": [], + "description": "", + "enabled": true + } + ] +} +``` + +--- + +## Create or replace a definition + +**`PUT /automation/definitions/{name}`** + +Upserts an automation definition by name. + +### Path parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `name` | string | Unique automation name | + +### Request body + +```json +{ + "name": "greet_on_request", + "triggers": ["user.request"], + "steps": ["greet"], + "variables": ["request_id"], + "description": "Greet users on request", + "enabled": true +} +``` + +### Response `200` — definition stored + +--- + +## Delete a definition + +**`DELETE /automation/definitions/{name}`** + +Removes a definition. + +### Responses + +| Status | Description | +|--------|-------------| +| `204` | Deleted | +| `404` | Not found | + +--- + +## Enable / Disable a definition + +**`POST /automation/definitions/{name}/enable`** +**`POST /automation/definitions/{name}/disable`** + +Both return `204` on success. + +--- + +## List run history + +**`GET /automation/runs?limit=50`** + +Returns past run records, newest first. + +| Query param | Type | Default | Max | +|-------------|------|---------|-----| +| `limit` | integer | 50 | 1000 | + +--- + +## Aggregate stats + +**`GET /automation/stats`** + +```json +{ + "total_runs": 142, + "automations": 5, + "components": 12, + "variables": 3, + "by_status": { + "success": 138, + "failed": 4 + } +} +``` + +--- + +## Status values + +| Value | Description | +|-------|-------------| +| `pending` | Queued, not yet started | +| `running` | Currently executing | +| `success` | All steps completed successfully | +| `failed` | One or more steps errored | +| `skipped` | Automation was disabled at trigger time | diff --git a/docs/guides/getting-started.md b/docs/guides/getting-started.md new file mode 100644 index 0000000..3e708d2 --- /dev/null +++ b/docs/guides/getting-started.md @@ -0,0 +1,116 @@ +--- +layout: default +title: Getting Started +nav_order: 2 +--- + +# Getting Started + +## Prerequisites + +- Python 3.11+ +- pip + +## Installation + +Clone the repository and install dependencies: + +```bash +git clone https://github.com/Pmaster-dev/server.git +cd server +pip install -r requirements.txt +``` + +## Using the automation engine + +The `src/automation` package is a self-contained, serverless automation engine. Set `PYTHONPATH=src` so Python can locate it without installing it as a package. + +```bash +export PYTHONPATH=src +``` + +### Define and trigger an automation + +```python +from automation import AutomationEngine, AutomationDefinition + +engine = AutomationEngine() + +# 1. Register a component function +engine.register_fn("greet", lambda inp: f"Hello, {inp.payload}!") + +# 2. Define an automation that fires on the "user.request" event +engine.define(AutomationDefinition( + name="greet_on_request", + triggers=["user.request"], + steps=["greet"], +)) + +# 3. Trigger the event +results = engine.trigger_type("user.request", payload="world") +print(results[0].status) # RunStatus.SUCCESS +print(results[0].outputs[0].result) # Hello, world! +``` + +### Generator variables + +Variables inject dynamic, lazily-evaluated values into components at runtime. + +```python +from automation import AutomationEngine, AutomationDefinition + +engine = AutomationEngine() + +# Define a generator variable (values are pulled on demand) +engine.define_variable("request_id", lambda: (f"req-{i}" for i in range(9999))) + +engine.register_fn("log", lambda inp: f"Processing {inp.variables['request_id']}") + +engine.define(AutomationDefinition( + name="log_request", + triggers=["api.call"], + steps=["log"], + variables=["request_id"], +)) + +results = engine.trigger_type("api.call") +print(results[0].outputs[0].result) # Processing req-0 +``` + +### Enable / disable automations + +```python +engine.disable("greet_on_request") +engine.enable("greet_on_request") +``` + +## Auth utilities + +The `auth/utils.py` module provides JWT token creation/verification, bcrypt password hashing, and session helpers. It depends on Flask and `flask-jwt-extended`. + +```python +from auth.utils import PasswordUtils, JWTUtils + +hashed = PasswordUtils.hash_password("supersecret") +assert PasswordUtils.verify_password("supersecret", hashed) + +access_token, refresh_token = JWTUtils.create_tokens("user-123", "alice") +payload = JWTUtils.decode_token(access_token) +print(payload["username"]) # alice +``` + +> **Note**: Set the `JWT_SECRET_KEY` environment variable in production. The default value is insecure. + +## Environment variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `JWT_SECRET_KEY` | Yes (prod) | Secret key used to sign JWT tokens | +| `REDIS_URL` | Yes (prod) | Redis connection URL for session caching | + +## Running type checks + +```bash +pip install pyre-check +pyre --source-directory src --source-directory auth check +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..f71d1a2 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,34 @@ +--- +layout: default +title: Home +nav_order: 1 +--- + +# Pmaster-dev / server + +Infrastructure and automation layer for the **Pmaster-dev / pinkycollie** ecosystem. + +## What's here + +| Module | Path | Purpose | +|--------|------|---------| +| Automation engine | `src/automation/` | Serverless Python automation with pluggable components and generator variables | +| Handoff module | `src/handoff/` | Cross-service handoff coordination | +| Auth utilities | `auth/utils.py` | JWT, bcrypt password hashing, session management | +| OpenAPI contracts | `docs/openapi/` | Machine-readable API contracts consumed by downstream services | + +## Getting started + +→ [Getting started guide](guides/getting-started) + +## API reference + +→ [Automation API](api/automation) + +## Ecosystem + +→ [Cross-org ecosystem inventory](pinkycollie-ecosystem-inventory) + +--- + +*Source: [github.com/Pmaster-dev/server](https://github.com/Pmaster-dev/server)* diff --git a/domnornalizer.php b/domnornalizer.php deleted file mode 100644 index 31b104d..0000000 --- a/domnornalizer.php +++ /dev/null @@ -1,2 +0,0 @@ - -domnormalizer.php diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..400da0e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +bcrypt>=4.0.1 +PyJWT>=2.8.0 +Flask>=3.0.0 +flask-jwt-extended>=4.6.0 +redis>=5.0.0 \ No newline at end of file diff --git a/spec/helpers b/spec/helpers deleted file mode 100644 index 8b13789..0000000 --- a/spec/helpers +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/automation b/types/automation deleted file mode 100644 index ead442d..0000000 --- a/types/automation +++ /dev/null @@ -1 +0,0 @@ -automation diff --git a/types/build b/types/build deleted file mode 100644 index 8b13789..0000000 --- a/types/build +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/file b/types/file deleted file mode 100644 index 8b13789..0000000 --- a/types/file +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/http b/types/http deleted file mode 100644 index 8b13789..0000000 --- a/types/http +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/language b/types/language deleted file mode 100644 index 8b13789..0000000 --- a/types/language +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/mail.md b/types/mail.md deleted file mode 100644 index 8b13789..0000000 --- a/types/mail.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/media.md b/types/media.md deleted file mode 100644 index 8b13789..0000000 --- a/types/media.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/types/oauth b/types/oauth deleted file mode 100644 index 23ad0ab..0000000 --- a/types/oauth +++ /dev/null @@ -1 +0,0 @@ -types/oauth