epsiloneridani 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.
- epsiloneridani-0.1.0/.dockerignore +14 -0
- epsiloneridani-0.1.0/.github/workflows/ci.yml +189 -0
- epsiloneridani-0.1.0/.gitignore +22 -0
- epsiloneridani-0.1.0/Dockerfile +94 -0
- epsiloneridani-0.1.0/PKG-INFO +7 -0
- epsiloneridani-0.1.0/README.md +382 -0
- epsiloneridani-0.1.0/compose.yaml +88 -0
- epsiloneridani-0.1.0/docs/docker.md +146 -0
- epsiloneridani-0.1.0/docs/quota.md +119 -0
- epsiloneridani-0.1.0/docs/reference.md +221 -0
- epsiloneridani-0.1.0/docs/sandbox.md +87 -0
- epsiloneridani-0.1.0/docs/stuck-review-diagnostics.md +55 -0
- epsiloneridani-0.1.0/docs/workers.md +347 -0
- epsiloneridani-0.1.0/epsiloneridani +22 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/__init__.py +74 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/__main__.py +9 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/agents.py +2030 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/build_caches.py +159 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/cli.py +1149 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/config.py +249 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/constants.py +310 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/github.py +754 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/intentions.py +163 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/loop.py +409 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/oauth.py +379 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/paths.py +94 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/quota.py +2255 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/review_diagnostics.py +350 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/review_state.py +389 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/round.py +311 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/runtime_status.py +89 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/survey.py +837 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/transcript.py +527 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/tui.py +876 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/usage.py +421 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/work_units.py +1587 -0
- epsiloneridani-0.1.0/epsiloneridani_worker/worker_manager.py +1843 -0
- epsiloneridani-0.1.0/prompts/bump.md +56 -0
- epsiloneridani-0.1.0/prompts/fix-ci.md +68 -0
- epsiloneridani-0.1.0/prompts/fix.md +48 -0
- epsiloneridani-0.1.0/prompts/lint-repair.md +61 -0
- epsiloneridani-0.1.0/prompts/rebase.md +44 -0
- epsiloneridani-0.1.0/prompts/roadmap.md +101 -0
- epsiloneridani-0.1.0/pyproject.toml +46 -0
- epsiloneridani-0.1.0/scripts/claim.sh +174 -0
- epsiloneridani-0.1.0/scripts/docker-entrypoint +42 -0
- epsiloneridani-0.1.0/scripts/gh-safe-pr-create +60 -0
- epsiloneridani-0.1.0/scripts/git-safe-push +57 -0
- epsiloneridani-0.1.0/scripts/oauth_refresh_loop.py +93 -0
- epsiloneridani-0.1.0/scripts/share-build-caches +183 -0
- epsiloneridani-0.1.0/tauceti +1 -0
- epsiloneridani-0.1.0/tests/agent_cmds.py +373 -0
- epsiloneridani-0.1.0/tests/agent_transcript.py +384 -0
- epsiloneridani-0.1.0/tests/authoring_profiles.py +240 -0
- epsiloneridani-0.1.0/tests/auto_stage_priority.py +146 -0
- epsiloneridani-0.1.0/tests/branch_claim_repo.py +245 -0
- epsiloneridani-0.1.0/tests/bubble_cache.py +269 -0
- epsiloneridani-0.1.0/tests/bubble_version.py +98 -0
- epsiloneridani-0.1.0/tests/build_status_rollup.py +113 -0
- epsiloneridani-0.1.0/tests/claims_repo.py +172 -0
- epsiloneridani-0.1.0/tests/claude_config_dir.py +103 -0
- epsiloneridani-0.1.0/tests/claude_keychain.py +281 -0
- epsiloneridani-0.1.0/tests/codex_account.py +384 -0
- epsiloneridani-0.1.0/tests/codex_fallback.py +131 -0
- epsiloneridani-0.1.0/tests/contest_claim.py +84 -0
- epsiloneridani-0.1.0/tests/dashboard.py +526 -0
- epsiloneridani-0.1.0/tests/dispatch_revalidation.py +229 -0
- epsiloneridani-0.1.0/tests/docker_entrypoint.sh +59 -0
- epsiloneridani-0.1.0/tests/egress.sh +32 -0
- epsiloneridani-0.1.0/tests/fix_diagnostic.py +141 -0
- epsiloneridani-0.1.0/tests/fixtures/codex_exec_0_146.jsonl +4 -0
- epsiloneridani-0.1.0/tests/fixtures/pr_selectors.json +11 -0
- epsiloneridani-0.1.0/tests/fork_authoring.py +461 -0
- epsiloneridani-0.1.0/tests/github_failure_reason.py +68 -0
- epsiloneridani-0.1.0/tests/github_ratelimit.py +230 -0
- epsiloneridani-0.1.0/tests/host_agent_preflight.py +159 -0
- epsiloneridani-0.1.0/tests/ignore_quota_hardblock.py +194 -0
- epsiloneridani-0.1.0/tests/inflight_review.py +84 -0
- epsiloneridani-0.1.0/tests/infra_failure_budget.py +167 -0
- epsiloneridani-0.1.0/tests/isolate_home_macos.py +235 -0
- epsiloneridani-0.1.0/tests/isolate_home_paths.py +99 -0
- epsiloneridani-0.1.0/tests/kiro.py +143 -0
- epsiloneridani-0.1.0/tests/ledger_blocking.py +73 -0
- epsiloneridani-0.1.0/tests/lifecycle.sh +65 -0
- epsiloneridani-0.1.0/tests/loop_sigterm.py +69 -0
- epsiloneridani-0.1.0/tests/mirror_creds.py +159 -0
- epsiloneridani-0.1.0/tests/mirror_creds_macos.py +107 -0
- epsiloneridani-0.1.0/tests/oauth_refresh_loop.py +284 -0
- epsiloneridani-0.1.0/tests/open_pr_pages.py +155 -0
- epsiloneridani-0.1.0/tests/pace_curve.py +364 -0
- epsiloneridani-0.1.0/tests/parity_selectors.py +150 -0
- epsiloneridani-0.1.0/tests/pr_targeting.py +542 -0
- epsiloneridani-0.1.0/tests/preflight_review_lake.py +50 -0
- epsiloneridani-0.1.0/tests/progress_tool_failure_log.py +180 -0
- epsiloneridani-0.1.0/tests/prompt_wrapper_paths.py +172 -0
- epsiloneridani-0.1.0/tests/provider_usage.py +161 -0
- epsiloneridani-0.1.0/tests/quota_auto_refresh.py +232 -0
- epsiloneridani-0.1.0/tests/quota_cache_fp.py +124 -0
- epsiloneridani-0.1.0/tests/quota_claude_bootstrap.py +520 -0
- epsiloneridani-0.1.0/tests/quota_claude_windows.py +418 -0
- epsiloneridani-0.1.0/tests/quota_codex_windows.py +170 -0
- epsiloneridani-0.1.0/tests/quota_launch_stage.py +208 -0
- epsiloneridani-0.1.0/tests/quota_loop_refresh.py +291 -0
- epsiloneridani-0.1.0/tests/quota_reason.py +265 -0
- epsiloneridani-0.1.0/tests/rebase_handoff.py +93 -0
- epsiloneridani-0.1.0/tests/review_codex_model.py +90 -0
- epsiloneridani-0.1.0/tests/review_daily_cap.py +76 -0
- epsiloneridani-0.1.0/tests/review_diagnostics.py +296 -0
- epsiloneridani-0.1.0/tests/review_provider_down.py +149 -0
- epsiloneridani-0.1.0/tests/review_scheduling.py +99 -0
- epsiloneridani-0.1.0/tests/review_state_freshness.py +304 -0
- epsiloneridani-0.1.0/tests/review_sync_failure.py +167 -0
- epsiloneridani-0.1.0/tests/review_throttles.py +205 -0
- epsiloneridani-0.1.0/tests/roadmap_backpressure.py +191 -0
- epsiloneridani-0.1.0/tests/roadmap_claims.py +150 -0
- epsiloneridani-0.1.0/tests/round_file_changes.py +151 -0
- epsiloneridani-0.1.0/tests/round_group_sweep.py +256 -0
- epsiloneridani-0.1.0/tests/rubric_bundle.py +124 -0
- epsiloneridani-0.1.0/tests/run-all +154 -0
- epsiloneridani-0.1.0/tests/safe_exists.py +43 -0
- epsiloneridani-0.1.0/tests/scoreboard_trust.py +145 -0
- epsiloneridani-0.1.0/tests/shared_build_caches.py +224 -0
- epsiloneridani-0.1.0/tests/source_argument.py +179 -0
- epsiloneridani-0.1.0/tests/spread_candidates.py +52 -0
- epsiloneridani-0.1.0/tests/ssl_cert_file.py +66 -0
- epsiloneridani-0.1.0/tests/status_labels.py +102 -0
- epsiloneridani-0.1.0/tests/tend_bot_permissions.py +87 -0
- epsiloneridani-0.1.0/tests/worker_claude_config.py +175 -0
- epsiloneridani-0.1.0/tests/worker_manager.py +507 -0
- epsiloneridani-0.1.0/workers.toml.example +20 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
schedule:
|
|
8
|
+
# Rebuild against moving base images and agent/install endpoints even when this repo is quiet.
|
|
9
|
+
- cron: "17 4 * * 1"
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ci-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
test:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
env:
|
|
19
|
+
# The worker is stdlib-only (bar the TUI's rich/textual), so test on one interpreter — the
|
|
20
|
+
# latest uv ships — rather than a per-version matrix. requires-python >=3.11 is a declaration.
|
|
21
|
+
UV_PYTHON: "3.14"
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
24
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
cache-suffix: test
|
|
28
|
+
- name: Run the test suite
|
|
29
|
+
run: ./tests/run-all
|
|
30
|
+
|
|
31
|
+
lint:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
35
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
36
|
+
with:
|
|
37
|
+
enable-cache: true
|
|
38
|
+
cache-suffix: lint
|
|
39
|
+
# Pin ruff so a new release's rules/formatter can't turn CI red without a code change here.
|
|
40
|
+
- name: ruff check
|
|
41
|
+
run: uvx ruff@0.15.18 check .
|
|
42
|
+
- name: ruff format --check
|
|
43
|
+
run: uvx ruff@0.15.18 format --check .
|
|
44
|
+
- name: Install ShellCheck
|
|
45
|
+
run: sudo apt-get update && sudo apt-get install -y shellcheck
|
|
46
|
+
# --severity=warning: skip the info/style notes on the deliberate `A && B || C` test idiom
|
|
47
|
+
# (where B, an echo, always succeeds), while still failing on real warnings and errors.
|
|
48
|
+
- name: ShellCheck
|
|
49
|
+
run: shellcheck --severity=warning scripts/claim.sh scripts/git-safe-push scripts/gh-safe-pr-create scripts/docker-entrypoint tests/*.sh tests/run-all
|
|
50
|
+
|
|
51
|
+
build:
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
55
|
+
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
56
|
+
with:
|
|
57
|
+
enable-cache: true
|
|
58
|
+
cache-suffix: build
|
|
59
|
+
- name: Build the wheel
|
|
60
|
+
run: uv build
|
|
61
|
+
# uv build proves the artifact builds; this proves the force-include mapping (prompts/ and
|
|
62
|
+
# scripts/ into the epsiloneridani_worker package) works once installed, and that the console script
|
|
63
|
+
# runs. Run the import check from /tmp with -P so the source-tree epsiloneridani_worker/ on the
|
|
64
|
+
# checkout root can't shadow the installed package (whose prompts/scripts sit beside it).
|
|
65
|
+
- name: Install smoke test
|
|
66
|
+
run: |
|
|
67
|
+
uv venv /tmp/smoke
|
|
68
|
+
uv pip install --python /tmp/smoke/bin/python dist/*.whl
|
|
69
|
+
/tmp/smoke/bin/epsiloneridani --help
|
|
70
|
+
cd /tmp && /tmp/smoke/bin/python -P - <<'PY'
|
|
71
|
+
import pathlib
|
|
72
|
+
import epsiloneridani_worker.__main__ as m
|
|
73
|
+
base = pathlib.Path(m.__file__).parent
|
|
74
|
+
assert (base / "prompts").is_dir(), "prompts/ missing from the installed wheel"
|
|
75
|
+
assert (base / "scripts").is_dir(), "scripts/ missing from the installed wheel"
|
|
76
|
+
print("bundled prompts/ and scripts/ present")
|
|
77
|
+
PY
|
|
78
|
+
|
|
79
|
+
docker:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
timeout-minutes: 30
|
|
82
|
+
steps:
|
|
83
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
84
|
+
- name: Validate the Compose model
|
|
85
|
+
run: docker compose config --quiet
|
|
86
|
+
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
|
|
87
|
+
- name: Build the worker image from this revision
|
|
88
|
+
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0
|
|
89
|
+
with:
|
|
90
|
+
context: .
|
|
91
|
+
load: true
|
|
92
|
+
tags: |
|
|
93
|
+
epsiloneridani-worker:ci
|
|
94
|
+
epsiloneridani-worker:local
|
|
95
|
+
pull: true
|
|
96
|
+
no-cache: ${{ github.event_name == 'schedule' }}
|
|
97
|
+
cache-from: type=gha
|
|
98
|
+
cache-to: type=gha,mode=min
|
|
99
|
+
- name: Smoke-test the image and Compose service
|
|
100
|
+
run: |
|
|
101
|
+
docker compose run --rm --no-deps \
|
|
102
|
+
-e HOME=/tmp/isolated-home \
|
|
103
|
+
epsiloneridani bash -euo pipefail -c '
|
|
104
|
+
mkdir -p "$HOME"
|
|
105
|
+
for tool in gh git uvx jq lake codex claude epsiloneridani-oauth-refresh; do
|
|
106
|
+
command -v "$tool" >/dev/null
|
|
107
|
+
done
|
|
108
|
+
lake --version
|
|
109
|
+
./epsiloneridani --help >/dev/null
|
|
110
|
+
claude auth login --help >/dev/null
|
|
111
|
+
codex login --help 2>&1 | grep -F -- --device-auth >/dev/null
|
|
112
|
+
doctor_output="$(./epsiloneridani doctor || true)"
|
|
113
|
+
printf "%s\n" "$doctor_output"
|
|
114
|
+
printf "%s\n" "$doctor_output" | grep -F "[ok ] lake"
|
|
115
|
+
git init -q /tmp/git-smoke
|
|
116
|
+
git -C /tmp/git-smoke commit --allow-empty -m smoke >/dev/null
|
|
117
|
+
test "$(git -C /tmp/git-smoke log -1 --format=%ae)" = "epsiloneridani-worker@users.noreply.github.com"
|
|
118
|
+
set +e
|
|
119
|
+
claude_output="$(timeout 15 claude -p "Reply with ok" --model opus --dangerously-skip-permissions 2>&1)"
|
|
120
|
+
set -e
|
|
121
|
+
! printf "%s\n" "$claude_output" | grep -F "cannot be used with root/sudo privileges"
|
|
122
|
+
'
|
|
123
|
+
- name: Smoke-test configurable worker options
|
|
124
|
+
run: |
|
|
125
|
+
TAUCETI_WORKER_ARGS=--help docker compose run --rm --no-deps epsiloneridani 2>&1 |
|
|
126
|
+
grep -F "usage: epsiloneridani work"
|
|
127
|
+
test ! -e .env
|
|
128
|
+
trap 'rm -f .env' EXIT
|
|
129
|
+
printf '%s\n' 'TAUCETI_WORKER_ARGS=--help' >.env
|
|
130
|
+
docker compose config --format json | python3 -c \
|
|
131
|
+
'import json, sys; assert json.load(sys.stdin)["services"]["epsiloneridani"]["environment"]["TAUCETI_WORKER_ARGS"] == "--help"'
|
|
132
|
+
# Exercise the actual tini -> uv -> Python process tree used by Compose. The
|
|
133
|
+
# unit test invokes Python directly and cannot prove that SIGTERM crosses uv.
|
|
134
|
+
- name: Graceful stop reaches the worker loop
|
|
135
|
+
run: |
|
|
136
|
+
docker compose up -d epsiloneridani
|
|
137
|
+
for attempt in $(seq 1 30); do
|
|
138
|
+
docker compose logs --no-color epsiloneridani | grep -F "loop start:" && break
|
|
139
|
+
test "$attempt" -lt 30
|
|
140
|
+
sleep 1
|
|
141
|
+
done
|
|
142
|
+
docker compose stop -t 60 epsiloneridani
|
|
143
|
+
worker_log="$(docker compose logs --no-color epsiloneridani)"
|
|
144
|
+
printf "%s\n" "$worker_log"
|
|
145
|
+
printf "%s\n" "$worker_log" | grep -F "loop terminated — stopping"
|
|
146
|
+
container_id="$(docker compose ps -aq epsiloneridani)"
|
|
147
|
+
test "$(docker inspect --format '{{.State.ExitCode}}' "$container_id")" = 143
|
|
148
|
+
- name: Smoke-test both credential refresher services
|
|
149
|
+
run: |
|
|
150
|
+
for provider in claude codex; do
|
|
151
|
+
log="$(mktemp)"
|
|
152
|
+
docker run --rm --entrypoint /usr/local/bin/epsiloneridani-oauth-refresh \
|
|
153
|
+
-e TAUCETI_REFRESH_ONCE=1 \
|
|
154
|
+
epsiloneridani-worker:ci "$provider" >"$log" 2>&1
|
|
155
|
+
grep -F "$provider-refresh: waiting for" "$log"
|
|
156
|
+
done
|
|
157
|
+
- name: Probe the live OAuth contracts
|
|
158
|
+
if: github.event_name == 'schedule'
|
|
159
|
+
run: |
|
|
160
|
+
docker run --rm -i --entrypoint python3 epsiloneridani-worker:ci <<'PY'
|
|
161
|
+
import requests
|
|
162
|
+
|
|
163
|
+
probes = [
|
|
164
|
+
(
|
|
165
|
+
"claude",
|
|
166
|
+
"https://platform.claude.com/v1/oauth/token",
|
|
167
|
+
{
|
|
168
|
+
"grant_type": "refresh_token",
|
|
169
|
+
"refresh_token": "invalid-ci-probe",
|
|
170
|
+
"client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
171
|
+
"scope": "user:profile user:inference user:sessions:claude_code user:mcp_servers",
|
|
172
|
+
},
|
|
173
|
+
),
|
|
174
|
+
(
|
|
175
|
+
"codex",
|
|
176
|
+
"https://auth.openai.com/oauth/token",
|
|
177
|
+
{
|
|
178
|
+
"grant_type": "refresh_token",
|
|
179
|
+
"refresh_token": "invalid-ci-probe",
|
|
180
|
+
"client_id": "app_EMoamEEZ73f0CkXaXp7hrann",
|
|
181
|
+
},
|
|
182
|
+
),
|
|
183
|
+
]
|
|
184
|
+
for name, url, payload in probes:
|
|
185
|
+
response = requests.post(url, json=payload, timeout=30)
|
|
186
|
+
assert 400 <= response.status_code < 500, (name, response.status_code)
|
|
187
|
+
assert isinstance(response.json(), dict), name
|
|
188
|
+
print(f"{name}: reachable OAuth endpoint rejected the invalid probe as expected")
|
|
189
|
+
PY
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Runtime state — not part of the worker's source.
|
|
2
|
+
# (Host authoring uses checkouts/; --bubble rounds run inside a container.)
|
|
3
|
+
/checkouts/
|
|
4
|
+
/state/
|
|
5
|
+
/logs/
|
|
6
|
+
|
|
7
|
+
# Local Docker Compose options (may contain operator-specific configuration).
|
|
8
|
+
/.env
|
|
9
|
+
|
|
10
|
+
# Legacy local Screen assignments; `epsiloneridani workers import workers.conf` migrates these.
|
|
11
|
+
/workers.conf
|
|
12
|
+
|
|
13
|
+
# Python bytecode (epsiloneridani is a single-file script; tests import it).
|
|
14
|
+
__pycache__/
|
|
15
|
+
*.pyc
|
|
16
|
+
|
|
17
|
+
# Build artifacts
|
|
18
|
+
/dist/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
|
|
21
|
+
# uv lockfile — generated for the single-file script, not source.
|
|
22
|
+
/uv.lock
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# A disposable Linux host for the Tau Ceti worker. The agent runs in host mode inside this
|
|
2
|
+
# container; it does not use the more restrictive Bubble sandbox.
|
|
3
|
+
FROM node:22-bookworm
|
|
4
|
+
|
|
5
|
+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
|
6
|
+
ARG TAUCETI_LEAN_TOOLCHAIN=leanprover/lean4:v4.32.0
|
|
7
|
+
|
|
8
|
+
# Runtime tools for epsiloneridani and its agents, plus a native toolchain for Lean builds. Debian
|
|
9
|
+
# package revisions deliberately track Bookworm's security repository instead of being frozen.
|
|
10
|
+
# hadolint ignore=DL3008
|
|
11
|
+
RUN apt-get update \
|
|
12
|
+
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
|
|
13
|
+
build-essential \
|
|
14
|
+
ca-certificates \
|
|
15
|
+
curl \
|
|
16
|
+
git \
|
|
17
|
+
jq \
|
|
18
|
+
python3 \
|
|
19
|
+
python3-requests \
|
|
20
|
+
ripgrep \
|
|
21
|
+
xz-utils \
|
|
22
|
+
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
23
|
+
-o /usr/share/keyrings/githubcli-archive-keyring.gpg \
|
|
24
|
+
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
25
|
+
> /etc/apt/sources.list.d/github-cli.list \
|
|
26
|
+
&& apt-get update \
|
|
27
|
+
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gh \
|
|
28
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
29
|
+
|
|
30
|
+
# Lean (elan/lake) and uv/uvx. Both installers select binaries for the build architecture.
|
|
31
|
+
RUN curl -fsSL https://elan.lean-lang.org/elan-init.sh | sh -s -- -y \
|
|
32
|
+
&& /root/.elan/bin/elan toolchain install "$TAUCETI_LEAN_TOOLCHAIN" \
|
|
33
|
+
&& /root/.elan/bin/elan default "$TAUCETI_LEAN_TOOLCHAIN" \
|
|
34
|
+
&& curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
35
|
+
|
|
36
|
+
# Subscription authentication is performed at runtime and persisted by compose.yaml. Pin the
|
|
37
|
+
# clients so rebuilding a deployment uses known client versions; the scheduled image build detects
|
|
38
|
+
# when a pinned client or its service contract stops working. Keep this after Lean so a client-version
|
|
39
|
+
# bump does not invalidate the much larger toolchain layer.
|
|
40
|
+
ARG CLAUDE_CODE_VERSION=2.1.220
|
|
41
|
+
ARG CODEX_VERSION=0.145.0
|
|
42
|
+
RUN npm install -g \
|
|
43
|
+
"@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
|
|
44
|
+
"@openai/codex@${CODEX_VERSION}"
|
|
45
|
+
|
|
46
|
+
# Kiro publishes native, architecture-specific bundles rather than an npm package. Pin both
|
|
47
|
+
# supported checksums and select the archive from Debian's build architecture. The arm64 musl
|
|
48
|
+
# bundle avoids the newer glibc required by Kiro's GNU arm64 build.
|
|
49
|
+
ARG KIRO_CLI_VERSION=2.16.1
|
|
50
|
+
ARG KIRO_CLI_SHA256_X64=6aeb89363c2c0b999cd1ad54449d632fa054ecce1dbba5ce175961877b3e0166
|
|
51
|
+
ARG KIRO_CLI_SHA256_ARM64=ad7c7e0c769bf89ae56d424811c83917a1130aa7f6559a133c5cdfc30c2f7c21
|
|
52
|
+
RUN case "$(dpkg --print-architecture)" in \
|
|
53
|
+
amd64) archive=kirocli-x86_64-linux.tar.xz; sha="$KIRO_CLI_SHA256_X64" ;; \
|
|
54
|
+
arm64) archive=kirocli-aarch64-linux-musl.tar.xz; sha="$KIRO_CLI_SHA256_ARM64" ;; \
|
|
55
|
+
*) echo "unsupported Kiro CLI architecture: $(dpkg --print-architecture)" >&2; exit 1 ;; \
|
|
56
|
+
esac \
|
|
57
|
+
&& curl -fsSL "https://prod.download.cli.kiro.dev/stable/${KIRO_CLI_VERSION}/${archive}" \
|
|
58
|
+
-o /tmp/kiro-cli.tar.xz \
|
|
59
|
+
&& echo "${sha} /tmp/kiro-cli.tar.xz" | sha256sum -c - \
|
|
60
|
+
&& tar -xJf /tmp/kiro-cli.tar.xz -C /tmp \
|
|
61
|
+
&& install -m 0755 /tmp/kirocli/bin/kiro-cli /usr/local/bin/kiro-cli \
|
|
62
|
+
&& install -m 0755 /tmp/kirocli/bin/kiro-cli-chat /usr/local/bin/kiro-cli-chat \
|
|
63
|
+
&& install -m 0755 /tmp/kirocli/bin/kiro-cli-term /usr/local/bin/kiro-cli-term \
|
|
64
|
+
&& rm -rf /tmp/kirocli /tmp/kiro-cli.tar.xz
|
|
65
|
+
|
|
66
|
+
ENV PATH="/root/.elan/bin:/root/.local/bin:${PATH}" \
|
|
67
|
+
ELAN_HOME=/root/.elan \
|
|
68
|
+
UV_CACHE_DIR=/root/.cache/uv \
|
|
69
|
+
DISABLE_AUTOUPDATER=1 \
|
|
70
|
+
IS_SANDBOX=1 \
|
|
71
|
+
PYTHONUNBUFFERED=1
|
|
72
|
+
|
|
73
|
+
# Keep the toolchain layers reusable while making every image contain the exact checked-out
|
|
74
|
+
# worker revision under test (including pull-request changes).
|
|
75
|
+
WORKDIR /opt/epsiloneridani
|
|
76
|
+
COPY epsiloneridani pyproject.toml ./
|
|
77
|
+
COPY prompts ./prompts
|
|
78
|
+
COPY scripts ./scripts
|
|
79
|
+
COPY epsiloneridani_worker ./epsiloneridani_worker
|
|
80
|
+
|
|
81
|
+
# The refresher is a symlink, not a copy: it imports the rotation core from epsiloneridani_worker, and
|
|
82
|
+
# resolving its own path back to /opt/epsiloneridani is how it finds the package in an image that runs the
|
|
83
|
+
# worker from a checkout rather than an installed wheel.
|
|
84
|
+
RUN ln -s /opt/epsiloneridani/scripts/oauth_refresh_loop.py /usr/local/bin/epsiloneridani-oauth-refresh \
|
|
85
|
+
&& install -m 0755 scripts/docker-entrypoint /usr/local/bin/epsiloneridani-entrypoint \
|
|
86
|
+
&& chmod 0755 epsiloneridani scripts/oauth_refresh_loop.py scripts/claim.sh scripts/gh-safe-pr-create scripts/git-safe-push \
|
|
87
|
+
&& ./epsiloneridani --help >/dev/null \
|
|
88
|
+
&& git config --system user.name "EpsilonEridani Worker" \
|
|
89
|
+
&& git config --system user.email "epsiloneridani-worker@users.noreply.github.com" \
|
|
90
|
+
&& git config --system credential.https://github.com.helper "" \
|
|
91
|
+
&& git config --system --add credential.https://github.com.helper "!gh auth git-credential"
|
|
92
|
+
|
|
93
|
+
ENTRYPOINT ["/usr/local/bin/epsiloneridani-entrypoint"]
|
|
94
|
+
CMD ["./epsiloneridani", "work", "--loop"]
|