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.
Files changed (130) hide show
  1. epsiloneridani-0.1.0/.dockerignore +14 -0
  2. epsiloneridani-0.1.0/.github/workflows/ci.yml +189 -0
  3. epsiloneridani-0.1.0/.gitignore +22 -0
  4. epsiloneridani-0.1.0/Dockerfile +94 -0
  5. epsiloneridani-0.1.0/PKG-INFO +7 -0
  6. epsiloneridani-0.1.0/README.md +382 -0
  7. epsiloneridani-0.1.0/compose.yaml +88 -0
  8. epsiloneridani-0.1.0/docs/docker.md +146 -0
  9. epsiloneridani-0.1.0/docs/quota.md +119 -0
  10. epsiloneridani-0.1.0/docs/reference.md +221 -0
  11. epsiloneridani-0.1.0/docs/sandbox.md +87 -0
  12. epsiloneridani-0.1.0/docs/stuck-review-diagnostics.md +55 -0
  13. epsiloneridani-0.1.0/docs/workers.md +347 -0
  14. epsiloneridani-0.1.0/epsiloneridani +22 -0
  15. epsiloneridani-0.1.0/epsiloneridani_worker/__init__.py +74 -0
  16. epsiloneridani-0.1.0/epsiloneridani_worker/__main__.py +9 -0
  17. epsiloneridani-0.1.0/epsiloneridani_worker/agents.py +2030 -0
  18. epsiloneridani-0.1.0/epsiloneridani_worker/build_caches.py +159 -0
  19. epsiloneridani-0.1.0/epsiloneridani_worker/cli.py +1149 -0
  20. epsiloneridani-0.1.0/epsiloneridani_worker/config.py +249 -0
  21. epsiloneridani-0.1.0/epsiloneridani_worker/constants.py +310 -0
  22. epsiloneridani-0.1.0/epsiloneridani_worker/github.py +754 -0
  23. epsiloneridani-0.1.0/epsiloneridani_worker/intentions.py +163 -0
  24. epsiloneridani-0.1.0/epsiloneridani_worker/loop.py +409 -0
  25. epsiloneridani-0.1.0/epsiloneridani_worker/oauth.py +379 -0
  26. epsiloneridani-0.1.0/epsiloneridani_worker/paths.py +94 -0
  27. epsiloneridani-0.1.0/epsiloneridani_worker/quota.py +2255 -0
  28. epsiloneridani-0.1.0/epsiloneridani_worker/review_diagnostics.py +350 -0
  29. epsiloneridani-0.1.0/epsiloneridani_worker/review_state.py +389 -0
  30. epsiloneridani-0.1.0/epsiloneridani_worker/round.py +311 -0
  31. epsiloneridani-0.1.0/epsiloneridani_worker/runtime_status.py +89 -0
  32. epsiloneridani-0.1.0/epsiloneridani_worker/survey.py +837 -0
  33. epsiloneridani-0.1.0/epsiloneridani_worker/transcript.py +527 -0
  34. epsiloneridani-0.1.0/epsiloneridani_worker/tui.py +876 -0
  35. epsiloneridani-0.1.0/epsiloneridani_worker/usage.py +421 -0
  36. epsiloneridani-0.1.0/epsiloneridani_worker/work_units.py +1587 -0
  37. epsiloneridani-0.1.0/epsiloneridani_worker/worker_manager.py +1843 -0
  38. epsiloneridani-0.1.0/prompts/bump.md +56 -0
  39. epsiloneridani-0.1.0/prompts/fix-ci.md +68 -0
  40. epsiloneridani-0.1.0/prompts/fix.md +48 -0
  41. epsiloneridani-0.1.0/prompts/lint-repair.md +61 -0
  42. epsiloneridani-0.1.0/prompts/rebase.md +44 -0
  43. epsiloneridani-0.1.0/prompts/roadmap.md +101 -0
  44. epsiloneridani-0.1.0/pyproject.toml +46 -0
  45. epsiloneridani-0.1.0/scripts/claim.sh +174 -0
  46. epsiloneridani-0.1.0/scripts/docker-entrypoint +42 -0
  47. epsiloneridani-0.1.0/scripts/gh-safe-pr-create +60 -0
  48. epsiloneridani-0.1.0/scripts/git-safe-push +57 -0
  49. epsiloneridani-0.1.0/scripts/oauth_refresh_loop.py +93 -0
  50. epsiloneridani-0.1.0/scripts/share-build-caches +183 -0
  51. epsiloneridani-0.1.0/tauceti +1 -0
  52. epsiloneridani-0.1.0/tests/agent_cmds.py +373 -0
  53. epsiloneridani-0.1.0/tests/agent_transcript.py +384 -0
  54. epsiloneridani-0.1.0/tests/authoring_profiles.py +240 -0
  55. epsiloneridani-0.1.0/tests/auto_stage_priority.py +146 -0
  56. epsiloneridani-0.1.0/tests/branch_claim_repo.py +245 -0
  57. epsiloneridani-0.1.0/tests/bubble_cache.py +269 -0
  58. epsiloneridani-0.1.0/tests/bubble_version.py +98 -0
  59. epsiloneridani-0.1.0/tests/build_status_rollup.py +113 -0
  60. epsiloneridani-0.1.0/tests/claims_repo.py +172 -0
  61. epsiloneridani-0.1.0/tests/claude_config_dir.py +103 -0
  62. epsiloneridani-0.1.0/tests/claude_keychain.py +281 -0
  63. epsiloneridani-0.1.0/tests/codex_account.py +384 -0
  64. epsiloneridani-0.1.0/tests/codex_fallback.py +131 -0
  65. epsiloneridani-0.1.0/tests/contest_claim.py +84 -0
  66. epsiloneridani-0.1.0/tests/dashboard.py +526 -0
  67. epsiloneridani-0.1.0/tests/dispatch_revalidation.py +229 -0
  68. epsiloneridani-0.1.0/tests/docker_entrypoint.sh +59 -0
  69. epsiloneridani-0.1.0/tests/egress.sh +32 -0
  70. epsiloneridani-0.1.0/tests/fix_diagnostic.py +141 -0
  71. epsiloneridani-0.1.0/tests/fixtures/codex_exec_0_146.jsonl +4 -0
  72. epsiloneridani-0.1.0/tests/fixtures/pr_selectors.json +11 -0
  73. epsiloneridani-0.1.0/tests/fork_authoring.py +461 -0
  74. epsiloneridani-0.1.0/tests/github_failure_reason.py +68 -0
  75. epsiloneridani-0.1.0/tests/github_ratelimit.py +230 -0
  76. epsiloneridani-0.1.0/tests/host_agent_preflight.py +159 -0
  77. epsiloneridani-0.1.0/tests/ignore_quota_hardblock.py +194 -0
  78. epsiloneridani-0.1.0/tests/inflight_review.py +84 -0
  79. epsiloneridani-0.1.0/tests/infra_failure_budget.py +167 -0
  80. epsiloneridani-0.1.0/tests/isolate_home_macos.py +235 -0
  81. epsiloneridani-0.1.0/tests/isolate_home_paths.py +99 -0
  82. epsiloneridani-0.1.0/tests/kiro.py +143 -0
  83. epsiloneridani-0.1.0/tests/ledger_blocking.py +73 -0
  84. epsiloneridani-0.1.0/tests/lifecycle.sh +65 -0
  85. epsiloneridani-0.1.0/tests/loop_sigterm.py +69 -0
  86. epsiloneridani-0.1.0/tests/mirror_creds.py +159 -0
  87. epsiloneridani-0.1.0/tests/mirror_creds_macos.py +107 -0
  88. epsiloneridani-0.1.0/tests/oauth_refresh_loop.py +284 -0
  89. epsiloneridani-0.1.0/tests/open_pr_pages.py +155 -0
  90. epsiloneridani-0.1.0/tests/pace_curve.py +364 -0
  91. epsiloneridani-0.1.0/tests/parity_selectors.py +150 -0
  92. epsiloneridani-0.1.0/tests/pr_targeting.py +542 -0
  93. epsiloneridani-0.1.0/tests/preflight_review_lake.py +50 -0
  94. epsiloneridani-0.1.0/tests/progress_tool_failure_log.py +180 -0
  95. epsiloneridani-0.1.0/tests/prompt_wrapper_paths.py +172 -0
  96. epsiloneridani-0.1.0/tests/provider_usage.py +161 -0
  97. epsiloneridani-0.1.0/tests/quota_auto_refresh.py +232 -0
  98. epsiloneridani-0.1.0/tests/quota_cache_fp.py +124 -0
  99. epsiloneridani-0.1.0/tests/quota_claude_bootstrap.py +520 -0
  100. epsiloneridani-0.1.0/tests/quota_claude_windows.py +418 -0
  101. epsiloneridani-0.1.0/tests/quota_codex_windows.py +170 -0
  102. epsiloneridani-0.1.0/tests/quota_launch_stage.py +208 -0
  103. epsiloneridani-0.1.0/tests/quota_loop_refresh.py +291 -0
  104. epsiloneridani-0.1.0/tests/quota_reason.py +265 -0
  105. epsiloneridani-0.1.0/tests/rebase_handoff.py +93 -0
  106. epsiloneridani-0.1.0/tests/review_codex_model.py +90 -0
  107. epsiloneridani-0.1.0/tests/review_daily_cap.py +76 -0
  108. epsiloneridani-0.1.0/tests/review_diagnostics.py +296 -0
  109. epsiloneridani-0.1.0/tests/review_provider_down.py +149 -0
  110. epsiloneridani-0.1.0/tests/review_scheduling.py +99 -0
  111. epsiloneridani-0.1.0/tests/review_state_freshness.py +304 -0
  112. epsiloneridani-0.1.0/tests/review_sync_failure.py +167 -0
  113. epsiloneridani-0.1.0/tests/review_throttles.py +205 -0
  114. epsiloneridani-0.1.0/tests/roadmap_backpressure.py +191 -0
  115. epsiloneridani-0.1.0/tests/roadmap_claims.py +150 -0
  116. epsiloneridani-0.1.0/tests/round_file_changes.py +151 -0
  117. epsiloneridani-0.1.0/tests/round_group_sweep.py +256 -0
  118. epsiloneridani-0.1.0/tests/rubric_bundle.py +124 -0
  119. epsiloneridani-0.1.0/tests/run-all +154 -0
  120. epsiloneridani-0.1.0/tests/safe_exists.py +43 -0
  121. epsiloneridani-0.1.0/tests/scoreboard_trust.py +145 -0
  122. epsiloneridani-0.1.0/tests/shared_build_caches.py +224 -0
  123. epsiloneridani-0.1.0/tests/source_argument.py +179 -0
  124. epsiloneridani-0.1.0/tests/spread_candidates.py +52 -0
  125. epsiloneridani-0.1.0/tests/ssl_cert_file.py +66 -0
  126. epsiloneridani-0.1.0/tests/status_labels.py +102 -0
  127. epsiloneridani-0.1.0/tests/tend_bot_permissions.py +87 -0
  128. epsiloneridani-0.1.0/tests/worker_claude_config.py +175 -0
  129. epsiloneridani-0.1.0/tests/worker_manager.py +507 -0
  130. epsiloneridani-0.1.0/workers.toml.example +20 -0
@@ -0,0 +1,14 @@
1
+ .git
2
+ .github
3
+ .env
4
+ .venv
5
+ .ruff_cache
6
+ __pycache__
7
+ *.pyc
8
+ *.egg-info
9
+ checkouts
10
+ dist
11
+ logs
12
+ state
13
+ tests
14
+ uv.lock
@@ -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"]
@@ -0,0 +1,7 @@
1
+ Metadata-Version: 2.4
2
+ Name: epsiloneridani
3
+ Version: 0.1.0
4
+ Summary: Autonomous worker for the eic/EpsilonEridani Lean library
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: rich>=13
7
+ Requires-Dist: textual<9,>=8