flaude 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.
- flaude-0.1.0/.env.example +15 -0
- flaude-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +49 -0
- flaude-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- flaude-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +18 -0
- flaude-0.1.0/.github/workflows/ci.yml +38 -0
- flaude-0.1.0/.github/workflows/docker.yml +49 -0
- flaude-0.1.0/.github/workflows/docs.yml +20 -0
- flaude-0.1.0/.github/workflows/publish.yml +23 -0
- flaude-0.1.0/.gitignore +16 -0
- flaude-0.1.0/.pre-commit-config.yaml +17 -0
- flaude-0.1.0/AGENTS.md +117 -0
- flaude-0.1.0/CHANGELOG.md +23 -0
- flaude-0.1.0/CLAUDE.md +46 -0
- flaude-0.1.0/CONTRIBUTING.md +61 -0
- flaude-0.1.0/LICENSE +21 -0
- flaude-0.1.0/PKG-INFO +342 -0
- flaude-0.1.0/README.md +282 -0
- flaude-0.1.0/SECURITY.md +26 -0
- flaude-0.1.0/TODOS.md +26 -0
- flaude-0.1.0/docs/api/app-machine.md +21 -0
- flaude-0.1.0/docs/api/concurrent.md +11 -0
- flaude-0.1.0/docs/api/configuration.md +9 -0
- flaude-0.1.0/docs/api/errors.md +7 -0
- flaude-0.1.0/docs/api/execution.md +17 -0
- flaude-0.1.0/docs/api/image.md +13 -0
- flaude-0.1.0/docs/api/log-infrastructure.md +19 -0
- flaude-0.1.0/docs/changelog.md +11 -0
- flaude-0.1.0/docs/concepts/architecture.md +117 -0
- flaude-0.1.0/docs/concepts/log-drain.md +154 -0
- flaude-0.1.0/docs/getting-started.md +156 -0
- flaude-0.1.0/docs/guide/building-on-flaude.md +214 -0
- flaude-0.1.0/docs/guide/concurrent.md +140 -0
- flaude-0.1.0/docs/guide/docker-image.md +103 -0
- flaude-0.1.0/docs/guide/error-handling.md +145 -0
- flaude-0.1.0/docs/guide/private-repos.md +100 -0
- flaude-0.1.0/docs/guide/streaming.md +130 -0
- flaude-0.1.0/docs/index.md +74 -0
- flaude-0.1.0/flaude/.dockerignore +4 -0
- flaude-0.1.0/flaude/Dockerfile +45 -0
- flaude-0.1.0/flaude/__init__.py +68 -0
- flaude-0.1.0/flaude/app.py +132 -0
- flaude-0.1.0/flaude/entrypoint.sh +130 -0
- flaude-0.1.0/flaude/executor.py +252 -0
- flaude-0.1.0/flaude/fly_client.py +147 -0
- flaude-0.1.0/flaude/image.py +250 -0
- flaude-0.1.0/flaude/lifecycle.py +325 -0
- flaude-0.1.0/flaude/log_drain.py +542 -0
- flaude-0.1.0/flaude/machine.py +217 -0
- flaude-0.1.0/flaude/machine_config.py +165 -0
- flaude-0.1.0/flaude/py.typed +0 -0
- flaude-0.1.0/flaude/runner.py +368 -0
- flaude-0.1.0/mkdocs.yml +85 -0
- flaude-0.1.0/pyproject.toml +99 -0
- flaude-0.1.0/seed.yaml +83 -0
- flaude-0.1.0/tests/__init__.py +0 -0
- flaude-0.1.0/tests/conftest.py +54 -0
- flaude-0.1.0/tests/test_app.py +235 -0
- flaude-0.1.0/tests/test_concurrent_integration.py +667 -0
- flaude-0.1.0/tests/test_e2e.py +239 -0
- flaude-0.1.0/tests/test_entrypoint.py +381 -0
- flaude-0.1.0/tests/test_executor.py +579 -0
- flaude-0.1.0/tests/test_exit_code_propagation.py +567 -0
- flaude-0.1.0/tests/test_failure_logs.py +352 -0
- flaude-0.1.0/tests/test_image.py +234 -0
- flaude-0.1.0/tests/test_lifecycle.py +778 -0
- flaude-0.1.0/tests/test_log_drain.py +465 -0
- flaude-0.1.0/tests/test_log_payload_parsing.py +622 -0
- flaude-0.1.0/tests/test_log_stream.py +342 -0
- flaude-0.1.0/tests/test_machine.py +365 -0
- flaude-0.1.0/tests/test_machine_cleanup_guarantee.py +653 -0
- flaude-0.1.0/tests/test_machine_config.py +243 -0
- flaude-0.1.0/tests/test_machine_create_config.py +400 -0
- flaude-0.1.0/tests/test_prompt_execution.py +348 -0
- flaude-0.1.0/tests/test_runner.py +611 -0
- flaude-0.1.0/tests/test_startup_env.py +349 -0
- flaude-0.1.0/thoughts/plans/2026-03-26-oss-requirements.md +1008 -0
- flaude-0.1.0/thoughts/research/2026-03-25-documentation-strategy.md +250 -0
- flaude-0.1.0/thoughts/research/2026-03-25-e2e-validation-automation.md +156 -0
- flaude-0.1.0/thoughts/research/2026-03-25-oss-requirements.md +212 -0
- flaude-0.1.0/uv.lock +1204 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Required for E2E tests (flaude/tests/test_e2e.py)
|
|
2
|
+
# Copy to .env: cp .env.example .env
|
|
3
|
+
|
|
4
|
+
# Fly.io API token — https://fly.io/user/personal_access_tokens
|
|
5
|
+
FLY_API_TOKEN=
|
|
6
|
+
|
|
7
|
+
# Claude Code OAuth token for authenticating Claude Code on Fly machines
|
|
8
|
+
CLAUDE_CODE_OAUTH_TOKEN=
|
|
9
|
+
|
|
10
|
+
# GitHub credentials (only needed if cloning private repos)
|
|
11
|
+
GITHUB_USERNAME=
|
|
12
|
+
GITHUB_TOKEN=
|
|
13
|
+
|
|
14
|
+
# Fly.io app name to use for E2E tests
|
|
15
|
+
FLY_APP_NAME=flaude-e2e
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug in flaude
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Thanks for reporting a bug! Please fill out the details below.
|
|
9
|
+
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: description
|
|
12
|
+
attributes:
|
|
13
|
+
label: What happened?
|
|
14
|
+
description: A clear description of the bug.
|
|
15
|
+
validations:
|
|
16
|
+
required: true
|
|
17
|
+
|
|
18
|
+
- type: textarea
|
|
19
|
+
id: reproduction
|
|
20
|
+
attributes:
|
|
21
|
+
label: Steps to reproduce
|
|
22
|
+
description: Minimal code to reproduce the issue.
|
|
23
|
+
render: python
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: expected
|
|
29
|
+
attributes:
|
|
30
|
+
label: Expected behavior
|
|
31
|
+
description: What did you expect to happen?
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
|
|
35
|
+
- type: input
|
|
36
|
+
id: version
|
|
37
|
+
attributes:
|
|
38
|
+
label: flaude version
|
|
39
|
+
placeholder: "e.g. 0.1.0"
|
|
40
|
+
validations:
|
|
41
|
+
required: true
|
|
42
|
+
|
|
43
|
+
- type: input
|
|
44
|
+
id: python
|
|
45
|
+
attributes:
|
|
46
|
+
label: Python version
|
|
47
|
+
placeholder: "e.g. 3.11.8"
|
|
48
|
+
validations:
|
|
49
|
+
required: true
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature or improvement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What problem does this solve?
|
|
9
|
+
description: Describe the use case or limitation you're hitting.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: solution
|
|
15
|
+
attributes:
|
|
16
|
+
label: Proposed solution
|
|
17
|
+
description: How would you like this to work?
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: alternatives
|
|
23
|
+
attributes:
|
|
24
|
+
label: Alternatives considered
|
|
25
|
+
description: Other approaches you've thought about.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- What does this PR do? Why? -->
|
|
4
|
+
|
|
5
|
+
## Changes
|
|
6
|
+
|
|
7
|
+
-
|
|
8
|
+
-
|
|
9
|
+
|
|
10
|
+
## Testing
|
|
11
|
+
|
|
12
|
+
- [ ] Unit tests pass (`make test`)
|
|
13
|
+
- [ ] Linting passes (`make check`)
|
|
14
|
+
- [ ] Added tests for new behavior (if applicable)
|
|
15
|
+
|
|
16
|
+
## Related issues
|
|
17
|
+
|
|
18
|
+
Closes #
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: astral-sh/setup-uv@v4
|
|
19
|
+
with:
|
|
20
|
+
enable-caching: true
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync --extra dev
|
|
23
|
+
- name: Run tests with coverage
|
|
24
|
+
run: uv run pytest --cov=flaude --cov-report=xml --cov-fail-under=90
|
|
25
|
+
|
|
26
|
+
lint:
|
|
27
|
+
name: Lint & Type Check
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
- uses: astral-sh/setup-uv@v4
|
|
32
|
+
with:
|
|
33
|
+
enable-caching: true
|
|
34
|
+
- run: uv sync --extra dev
|
|
35
|
+
- run: uv run ruff check .
|
|
36
|
+
- run: uv run ruff format --check .
|
|
37
|
+
- run: uv run mypy flaude/ tests/
|
|
38
|
+
- run: uv run bandit -r flaude/ -ll
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Docker
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
release:
|
|
7
|
+
types: [published]
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
REGISTRY: ghcr.io
|
|
11
|
+
IMAGE_NAME: ${{ github.repository }}
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build-and-push:
|
|
15
|
+
name: Build and push Docker image
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
packages: write
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Log in to GHCR
|
|
25
|
+
uses: docker/login-action@v3
|
|
26
|
+
with:
|
|
27
|
+
registry: ${{ env.REGISTRY }}
|
|
28
|
+
username: ${{ github.actor }}
|
|
29
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
30
|
+
|
|
31
|
+
- name: Extract metadata
|
|
32
|
+
id: meta
|
|
33
|
+
uses: docker/metadata-action@v5
|
|
34
|
+
with:
|
|
35
|
+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
|
36
|
+
tags: |
|
|
37
|
+
type=raw,value=latest,enable={{is_default_branch}}
|
|
38
|
+
type=sha,prefix=sha-
|
|
39
|
+
type=semver,pattern={{version}}
|
|
40
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
41
|
+
|
|
42
|
+
- name: Build and push
|
|
43
|
+
uses: docker/build-push-action@v5
|
|
44
|
+
with:
|
|
45
|
+
context: flaude
|
|
46
|
+
file: flaude/Dockerfile
|
|
47
|
+
push: true
|
|
48
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
49
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: Deploy docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
deploy:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.11"
|
|
19
|
+
- run: pip install ".[docs]"
|
|
20
|
+
- run: mkdocs gh-deploy --force
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
name: Build and publish to PyPI
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- uses: astral-sh/setup-uv@v4
|
|
18
|
+
|
|
19
|
+
- name: Build package
|
|
20
|
+
run: uv build
|
|
21
|
+
|
|
22
|
+
- name: Publish to PyPI
|
|
23
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
flaude-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.3.7
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff
|
|
6
|
+
args: [--fix]
|
|
7
|
+
- id: ruff-format
|
|
8
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
9
|
+
rev: v1.9.0
|
|
10
|
+
hooks:
|
|
11
|
+
- id: mypy
|
|
12
|
+
args: [--ignore-missing-imports]
|
|
13
|
+
additional_dependencies:
|
|
14
|
+
- pytest
|
|
15
|
+
- pytest-asyncio
|
|
16
|
+
- respx
|
|
17
|
+
- httpx
|
flaude-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# AGENTS.md — AI-optimized reference for flaude
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
|
|
5
|
+
- **Package name**: `flaude`
|
|
6
|
+
- **Purpose**: Python library for executing Claude Code prompts on ephemeral Fly.io machines
|
|
7
|
+
- **Language**: Python 3.11+
|
|
8
|
+
- **Single dependency**: httpx (async HTTP)
|
|
9
|
+
- **No CLI** — library-only, all async
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
flaude/
|
|
15
|
+
├── __init__.py # Public API (re-exports everything)
|
|
16
|
+
├── app.py # Fly.io app CRUD: ensure_app, create_app, get_app
|
|
17
|
+
├── runner.py # Core execution: run, run_and_destroy, wait_for_machine_exit
|
|
18
|
+
├── executor.py # Concurrent batch: ConcurrentExecutor, run_batch, run_one
|
|
19
|
+
├── lifecycle.py # Log-streaming execution: run_with_logs → StreamingRun
|
|
20
|
+
├── machine.py # Machine CRUD: create_machine, stop_machine, destroy_machine
|
|
21
|
+
├── machine_config.py # Config dataclasses: MachineConfig, RepoSpec, build_machine_config
|
|
22
|
+
├── fly_client.py # Low-level HTTP: fly_get, fly_post, fly_delete, fetch_machine_logs, FlyAPIError
|
|
23
|
+
├── log_drain.py # Log infrastructure: LogDrainServer, LogCollector, LogStream
|
|
24
|
+
├── image.py # Docker: docker_build, docker_push, ensure_image
|
|
25
|
+
├── Dockerfile # Container: Node.js 22 + Claude Code + git + gh CLI
|
|
26
|
+
└── entrypoint.sh # Container startup: clone repos → run claude -p → write exit marker
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Execution modes
|
|
30
|
+
|
|
31
|
+
### 1. Fire-and-forget (`run_and_destroy`)
|
|
32
|
+
Create machine → wait for exit → destroy. Returns `RunResult`. Raises `MachineExitError` on non-zero exit.
|
|
33
|
+
|
|
34
|
+
### 2. Streaming (`run_with_logs`)
|
|
35
|
+
Creates `LogDrainServer` before machine → subscribes to logs → returns `StreamingRun` (async iterator + context manager). Background task waits for exit, signals collector, destroys machine.
|
|
36
|
+
|
|
37
|
+
### 3. Concurrent (`ConcurrentExecutor.run_batch`)
|
|
38
|
+
Dispatches multiple `ExecutionRequest` objects via `asyncio.gather` with optional semaphore. Returns `BatchResult` with per-request `ExecutionResult`.
|
|
39
|
+
|
|
40
|
+
## Key types
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
# Configuration
|
|
44
|
+
MachineConfig(image, claude_code_oauth_token, github_username, github_token, prompt, repos, region, vm_size, vm_cpus, vm_memory_mb, auto_destroy, env, metadata)
|
|
45
|
+
RepoSpec(url, branch="", target_dir="")
|
|
46
|
+
|
|
47
|
+
# Results
|
|
48
|
+
RunResult(machine_id, exit_code, state, destroyed) # frozen dataclass
|
|
49
|
+
MachineExitError(machine_id, exit_code, state, logs) # exception
|
|
50
|
+
BatchResult(results, total, succeeded, failed) # frozen dataclass
|
|
51
|
+
ExecutionResult(tag, run_result, error) # .success property
|
|
52
|
+
|
|
53
|
+
# Execution
|
|
54
|
+
ExecutionRequest(config, name=None, tag="")
|
|
55
|
+
ConcurrentExecutor(app_name, token=None, max_concurrency=None, wait_timeout=3600.0)
|
|
56
|
+
StreamingRun # async iterator + context manager, .result(), .cleanup(), .machine_id
|
|
57
|
+
|
|
58
|
+
# Infrastructure
|
|
59
|
+
FlyApp(name, org, region)
|
|
60
|
+
FlyMachine(id, name, state, region, instance_id, app_name)
|
|
61
|
+
LogDrainServer(collector, host="0.0.0.0", port=0, include_stderr=False)
|
|
62
|
+
LogCollector() # machine_id → asyncio.Queue routing
|
|
63
|
+
LogStream(queue, item_timeout=None, total_timeout=None) # async iterator
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Critical invariants
|
|
67
|
+
|
|
68
|
+
1. **Guaranteed cleanup**: Every machine is destroyed in a `try/finally` block — exceptions, cancellations, and timeouts all trigger destruction.
|
|
69
|
+
2. **Log drain before machine**: `LogDrainServer` starts before `create_machine` so no early log lines are lost.
|
|
70
|
+
3. **Exit code fallback**: If Fly API doesn't report exit code, `entrypoint.sh` writes `[flaude:exit:N]` marker parsed by `extract_exit_code_from_logs`.
|
|
71
|
+
4. **Never raises in batch**: `ConcurrentExecutor._execute_one` catches all exceptions into `ExecutionResult.error`.
|
|
72
|
+
5. **Restart policy is "no"**: Machines run once and stop. `auto_destroy=True` by default.
|
|
73
|
+
|
|
74
|
+
## Container image
|
|
75
|
+
|
|
76
|
+
- **Base**: `node:22-bookworm-slim`
|
|
77
|
+
- **Pre-installed**: git, jq, gh (GitHub CLI), `@anthropic-ai/claude-code` (npm global)
|
|
78
|
+
- **Entrypoint flow**: validate `CLAUDE_CODE_OAUTH_TOKEN` → configure git credentials → clone repos from `FLAUDE_REPOS` JSON → `cd` to workspace → `claude -p -- "$FLAUDE_PROMPT"` → write exit marker → exit
|
|
79
|
+
|
|
80
|
+
## Environment variables
|
|
81
|
+
|
|
82
|
+
**Host-side** (your process):
|
|
83
|
+
- `FLY_API_TOKEN` — Fly.io API authentication
|
|
84
|
+
|
|
85
|
+
**Machine-side** (set automatically by `build_machine_config`):
|
|
86
|
+
- `CLAUDE_CODE_OAUTH_TOKEN` — Claude Code auth
|
|
87
|
+
- `FLAUDE_PROMPT` — The prompt string
|
|
88
|
+
- `FLAUDE_REPOS` — JSON array of `{url, branch?, target_dir?}`
|
|
89
|
+
- `GITHUB_USERNAME` / `GITHUB_TOKEN` — Git credentials (optional)
|
|
90
|
+
|
|
91
|
+
## Fly.io API interaction
|
|
92
|
+
|
|
93
|
+
All API calls go through `fly_client.py` which wraps httpx:
|
|
94
|
+
- **Machines API**: `https://api.machines.dev/v1` — auth via `Bearer {FLY_API_TOKEN}`
|
|
95
|
+
- **Platform API**: `https://api.fly.io` — auth via raw token as `Authorization` header (no `Bearer` prefix)
|
|
96
|
+
- Machine wait: `GET /apps/{app}/machines/{id}/wait?state=stopped` (long-poll), with polling fallback
|
|
97
|
+
- Log retrieval: `GET https://api.fly.io/api/v1/apps/{app}/logs?instance={machine_id}` — historical logs (~15 day retention), works after machine exit/destroy
|
|
98
|
+
- Terminal states: `stopped`, `destroyed`, `failed`
|
|
99
|
+
- Exit code extraction: `event["request"]["exit_event"]["exit_code"]` (or via `monitor_event` wrapper)
|
|
100
|
+
|
|
101
|
+
## Testing
|
|
102
|
+
|
|
103
|
+
- **Framework**: pytest + pytest-asyncio (auto mode)
|
|
104
|
+
- **HTTP mocking**: respx
|
|
105
|
+
- **18 test files** covering: app, machine, config, runner, executor, lifecycle, log drain, log stream, log parsing, entrypoint, exit codes, cleanup guarantees, concurrent integration, failure logs, image, startup env, prompt execution
|
|
106
|
+
- **Run unit tests**: `pytest` (E2E excluded by default via `addopts = "-m 'not e2e'"`)
|
|
107
|
+
- **Run E2E tests**: `source .env && pytest -m e2e -v`
|
|
108
|
+
- **E2E requires**: `FLY_API_TOKEN` + `CLAUDE_CODE_OAUTH_TOKEN` (both in `.env`) + Docker image pushed
|
|
109
|
+
- **E2E test files**: `tests/conftest.py` (fixtures), `tests/test_e2e.py` (5 tests)
|
|
110
|
+
- **E2E validates**: machine lifecycle, log retrieval via platform API, public/private repo clone, cleanup guarantee
|
|
111
|
+
- **Docker image**: Built with `--platform linux/amd64` (required by Fly.io, even on ARM hosts)
|
|
112
|
+
|
|
113
|
+
## Default machine spec
|
|
114
|
+
|
|
115
|
+
- VM size: `performance-2x` (2 shared CPUs, 4 GB RAM)
|
|
116
|
+
- Region: `iad` (US East)
|
|
117
|
+
- Image: `registry.fly.io/flaude:latest`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to flaude will be documented here.
|
|
4
|
+
|
|
5
|
+
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
Versions follow [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-03-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Initial release
|
|
14
|
+
- `MachineConfig` for configuring Fly.io machine execution parameters
|
|
15
|
+
- `run_and_destroy()` for fire-and-forget prompt execution
|
|
16
|
+
- `run_with_logs()` for streaming log output
|
|
17
|
+
- `ensure_app()` for idempotent Fly.io app creation
|
|
18
|
+
- Automatic machine cleanup via `try/finally` guarantee
|
|
19
|
+
- Support for cloning multiple repos into `/workspace`
|
|
20
|
+
- Concurrent execution support
|
|
21
|
+
|
|
22
|
+
[Unreleased]: https://github.com/ravi-hq/flaude/compare/v0.1.0...HEAD
|
|
23
|
+
[0.1.0]: https://github.com/ravi-hq/flaude/releases/tag/v0.1.0
|
flaude-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<!-- ooo:START -->
|
|
2
|
+
<!-- ooo:VERSION:0.14.0 -->
|
|
3
|
+
# Ouroboros — Specification-First AI Development
|
|
4
|
+
|
|
5
|
+
> Before telling AI what to build, define what should be built.
|
|
6
|
+
> As Socrates asked 2,500 years ago — "What do you truly know?"
|
|
7
|
+
> Ouroboros turns that question into an evolutionary AI workflow engine.
|
|
8
|
+
|
|
9
|
+
Most AI coding fails at the input, not the output. Ouroboros fixes this by
|
|
10
|
+
**exposing hidden assumptions before any code is written**.
|
|
11
|
+
|
|
12
|
+
1. **Socratic Clarity** — Question until ambiguity ≤ 0.2
|
|
13
|
+
2. **Ontological Precision** — Solve the root problem, not symptoms
|
|
14
|
+
3. **Evolutionary Loops** — Each evaluation cycle feeds back into better specs
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Interview → Seed → Execute → Evaluate
|
|
18
|
+
↑ ↓
|
|
19
|
+
└─── Evolutionary Loop ─────┘
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## ooo Commands
|
|
23
|
+
|
|
24
|
+
Each command loads its agent/MCP on-demand. Details in each skill file.
|
|
25
|
+
|
|
26
|
+
| Command | Loads |
|
|
27
|
+
|---------|-------|
|
|
28
|
+
| `ooo` | — |
|
|
29
|
+
| `ooo interview` | `ouroboros:socratic-interviewer` |
|
|
30
|
+
| `ooo seed` | `ouroboros:seed-architect` |
|
|
31
|
+
| `ooo run` | MCP required |
|
|
32
|
+
| `ooo evolve` | MCP: `evolve_step` |
|
|
33
|
+
| `ooo evaluate` | `ouroboros:evaluator` |
|
|
34
|
+
| `ooo unstuck` | `ouroboros:{persona}` |
|
|
35
|
+
| `ooo status` | MCP: `session_status` |
|
|
36
|
+
| `ooo setup` | — |
|
|
37
|
+
| `ooo help` | — |
|
|
38
|
+
|
|
39
|
+
## Agents
|
|
40
|
+
|
|
41
|
+
Loaded on-demand — not preloaded.
|
|
42
|
+
|
|
43
|
+
**Core**: socratic-interviewer, ontologist, seed-architect, evaluator,
|
|
44
|
+
wonder, reflect, advocate, contrarian, judge
|
|
45
|
+
**Support**: hacker, simplifier, researcher, architect
|
|
46
|
+
<!-- ooo:END -->
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Contributing to flaude
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing!
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
**Prerequisites**: Python 3.11+, [uv](https://docs.astral.sh/uv/), a Fly.io account (for E2E tests only).
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/ravi-hq/flaude.git
|
|
11
|
+
cd flaude
|
|
12
|
+
uv sync --extra dev
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Running tests
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Unit tests (no external dependencies)
|
|
19
|
+
uv run pytest
|
|
20
|
+
|
|
21
|
+
# With coverage
|
|
22
|
+
uv run pytest --cov=flaude
|
|
23
|
+
|
|
24
|
+
# E2E tests (requires FLY_API_TOKEN and CLAUDE_CODE_OAUTH_TOKEN in .env)
|
|
25
|
+
cp .env.example .env # fill in your credentials
|
|
26
|
+
uv run pytest -m e2e
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Linting and type checking
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv run ruff check .
|
|
33
|
+
uv run ruff format --check .
|
|
34
|
+
uv run mypy flaude
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Submitting a pull request
|
|
38
|
+
|
|
39
|
+
1. Fork the repo and create a branch from `main`.
|
|
40
|
+
2. Make your changes with tests for any new behaviour.
|
|
41
|
+
3. Run the linter and tests locally — CI must pass.
|
|
42
|
+
4. Open a PR against `main` with a clear description of what and why.
|
|
43
|
+
5. A maintainer will review and merge.
|
|
44
|
+
|
|
45
|
+
## Reporting bugs
|
|
46
|
+
|
|
47
|
+
Use the [bug report template](.github/ISSUE_TEMPLATE/bug_report.yml).
|
|
48
|
+
|
|
49
|
+
## Requesting features
|
|
50
|
+
|
|
51
|
+
Use the [feature request template](.github/ISSUE_TEMPLATE/feature_request.yml).
|
|
52
|
+
|
|
53
|
+
## Code style
|
|
54
|
+
|
|
55
|
+
- Black-compatible formatting enforced by `ruff format`.
|
|
56
|
+
- Type annotations required on all public functions.
|
|
57
|
+
- Async-first: use `httpx.AsyncClient` and `asyncio`; avoid blocking calls.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
By contributing you agree that your contributions will be licensed under the [MIT License](LICENSE).
|
flaude-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 flaude contributors
|
|
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.
|