@unimatrix27/ralph-harness 1.0.0
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.
- package/CONTRIBUTING.md +89 -0
- package/README.md +401 -0
- package/dist/bin/ralph-bootstrap-aws.d.ts +3 -0
- package/dist/bin/ralph-bootstrap-aws.d.ts.map +1 -0
- package/dist/bin/ralph-bootstrap-aws.js +43 -0
- package/dist/bin/ralph-bootstrap-aws.js.map +1 -0
- package/dist/bin/ralph-fire.d.ts +3 -0
- package/dist/bin/ralph-fire.d.ts.map +1 -0
- package/dist/bin/ralph-fire.js +59 -0
- package/dist/bin/ralph-fire.js.map +1 -0
- package/dist/bin/ralph-gsm.d.ts +3 -0
- package/dist/bin/ralph-gsm.d.ts.map +1 -0
- package/dist/bin/ralph-gsm.js +93 -0
- package/dist/bin/ralph-gsm.js.map +1 -0
- package/dist/bin/ralph-orchestrate.d.ts +3 -0
- package/dist/bin/ralph-orchestrate.d.ts.map +1 -0
- package/dist/bin/ralph-orchestrate.js +20 -0
- package/dist/bin/ralph-orchestrate.js.map +1 -0
- package/dist/bin/ralph-sync-credential.d.ts +3 -0
- package/dist/bin/ralph-sync-credential.d.ts.map +1 -0
- package/dist/bin/ralph-sync-credential.js +44 -0
- package/dist/bin/ralph-sync-credential.js.map +1 -0
- package/dist/bin/ralph-sync-github-pat.d.ts +3 -0
- package/dist/bin/ralph-sync-github-pat.d.ts.map +1 -0
- package/dist/bin/ralph-sync-github-pat.js +93 -0
- package/dist/bin/ralph-sync-github-pat.js.map +1 -0
- package/dist/bin/ralph-tail-logs.d.ts +3 -0
- package/dist/bin/ralph-tail-logs.d.ts.map +1 -0
- package/dist/bin/ralph-tail-logs.js +72 -0
- package/dist/bin/ralph-tail-logs.js.map +1 -0
- package/dist/bin/ralph-validate-config.d.ts +3 -0
- package/dist/bin/ralph-validate-config.d.ts.map +1 -0
- package/dist/bin/ralph-validate-config.js +41 -0
- package/dist/bin/ralph-validate-config.js.map +1 -0
- package/dist/lib/aws-bootstrap.d.ts +53 -0
- package/dist/lib/aws-bootstrap.d.ts.map +1 -0
- package/dist/lib/aws-bootstrap.js +438 -0
- package/dist/lib/aws-bootstrap.js.map +1 -0
- package/dist/lib/aws-clients.d.ts +17 -0
- package/dist/lib/aws-clients.d.ts.map +1 -0
- package/dist/lib/aws-clients.js +25 -0
- package/dist/lib/aws-clients.js.map +1 -0
- package/dist/lib/claude-runner.d.ts +21 -0
- package/dist/lib/claude-runner.d.ts.map +1 -0
- package/dist/lib/claude-runner.js +101 -0
- package/dist/lib/claude-runner.js.map +1 -0
- package/dist/lib/credential-syncer.d.ts +27 -0
- package/dist/lib/credential-syncer.d.ts.map +1 -0
- package/dist/lib/credential-syncer.js +116 -0
- package/dist/lib/credential-syncer.js.map +1 -0
- package/dist/lib/ec2-orchestrator.d.ts +38 -0
- package/dist/lib/ec2-orchestrator.d.ts.map +1 -0
- package/dist/lib/ec2-orchestrator.js +469 -0
- package/dist/lib/ec2-orchestrator.js.map +1 -0
- package/dist/lib/env-loader.d.ts +18 -0
- package/dist/lib/env-loader.d.ts.map +1 -0
- package/dist/lib/env-loader.js +120 -0
- package/dist/lib/env-loader.js.map +1 -0
- package/dist/lib/fire-launcher.d.ts +59 -0
- package/dist/lib/fire-launcher.d.ts.map +1 -0
- package/dist/lib/fire-launcher.js +320 -0
- package/dist/lib/fire-launcher.js.map +1 -0
- package/dist/lib/gh-runner.d.ts +13 -0
- package/dist/lib/gh-runner.d.ts.map +1 -0
- package/dist/lib/gh-runner.js +50 -0
- package/dist/lib/gh-runner.js.map +1 -0
- package/dist/lib/github-state-mutator.d.ts +11 -0
- package/dist/lib/github-state-mutator.d.ts.map +1 -0
- package/dist/lib/github-state-mutator.js +179 -0
- package/dist/lib/github-state-mutator.js.map +1 -0
- package/dist/lib/phase-result-schemas.d.ts +88 -0
- package/dist/lib/phase-result-schemas.d.ts.map +1 -0
- package/dist/lib/phase-result-schemas.js +180 -0
- package/dist/lib/phase-result-schemas.js.map +1 -0
- package/dist/lib/post-hoc-agent-stuck-checker.d.ts +26 -0
- package/dist/lib/post-hoc-agent-stuck-checker.d.ts.map +1 -0
- package/dist/lib/post-hoc-agent-stuck-checker.js +142 -0
- package/dist/lib/post-hoc-agent-stuck-checker.js.map +1 -0
- package/dist/lib/prompt-renderer.d.ts +4 -0
- package/dist/lib/prompt-renderer.d.ts.map +1 -0
- package/dist/lib/prompt-renderer.js +30 -0
- package/dist/lib/prompt-renderer.js.map +1 -0
- package/dist/lib/security-runner.d.ts +7 -0
- package/dist/lib/security-runner.d.ts.map +1 -0
- package/dist/lib/security-runner.js +53 -0
- package/dist/lib/security-runner.js.map +1 -0
- package/dist/lib/structured-log-emitter.d.ts +53 -0
- package/dist/lib/structured-log-emitter.d.ts.map +1 -0
- package/dist/lib/structured-log-emitter.js +122 -0
- package/dist/lib/structured-log-emitter.js.map +1 -0
- package/dist/lib/target-config-schema.d.ts +28 -0
- package/dist/lib/target-config-schema.d.ts.map +1 -0
- package/dist/lib/target-config-schema.js +157 -0
- package/dist/lib/target-config-schema.js.map +1 -0
- package/dist/lib/user-data-renderer.d.ts +20 -0
- package/dist/lib/user-data-renderer.d.ts.map +1 -0
- package/dist/lib/user-data-renderer.js +75 -0
- package/dist/lib/user-data-renderer.js.map +1 -0
- package/lib/cloud-init/system-setup.sh +338 -0
- package/package.json +55 -0
- package/prompts/discovery.md +182 -0
- package/prompts/implementation.md +161 -0
- package/prompts/review.md +135 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Contributing to ralph-harness
|
|
2
|
+
|
|
3
|
+
## Public-safe contract (read this first)
|
|
4
|
+
|
|
5
|
+
**No target-specific identifiers in this repository, ever.** This includes:
|
|
6
|
+
|
|
7
|
+
- organization or company names
|
|
8
|
+
- repository names of any specific target
|
|
9
|
+
- product, service, brand, or domain-specific terms tied to a particular target
|
|
10
|
+
- usernames or email addresses tied to a specific target's people or bots
|
|
11
|
+
- specific URLs, hostnames, or AWS account IDs
|
|
12
|
+
- copy-pasted code, prompts, or fixtures that name a specific target
|
|
13
|
+
|
|
14
|
+
The harness is generic by design: every target-specific knob is supplied at
|
|
15
|
+
runtime through the target repository's `.ralph/config.yaml` (see
|
|
16
|
+
[`docs/config-schema.md`](docs/config-schema.md)) and through environment
|
|
17
|
+
variables / SSM parameters. If you find yourself needing to commit something
|
|
18
|
+
target-specific to make a change land — stop and reshape the change so the
|
|
19
|
+
target detail moves into config instead.
|
|
20
|
+
|
|
21
|
+
PR reviewers must reject any change that introduces a target-specific
|
|
22
|
+
identifier. There is no automated check yet; this is enforced by review and by
|
|
23
|
+
the rule above. If you spot an existing leak, open an issue or PR to remove it.
|
|
24
|
+
|
|
25
|
+
## Repository layout
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
bin/ user-facing CLI entry points
|
|
29
|
+
lib/ sourceable bash modules
|
|
30
|
+
docs/ schema and design docs
|
|
31
|
+
tests/ bats-core tests
|
|
32
|
+
tests/fixtures/ yaml fixtures for the tests
|
|
33
|
+
.ralph/ (target repos only — not used by the harness itself)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Development dependencies
|
|
37
|
+
|
|
38
|
+
- `bash` 4+ (or recent macOS bash; the modules assume `[[`-style conditionals)
|
|
39
|
+
- [`yq`](https://github.com/mikefarah/yq) v4 (the Go binary by mikefarah)
|
|
40
|
+
- `jq` (used by `gh --jq` in the github-state-mutator module)
|
|
41
|
+
- `gh` (GitHub CLI, authenticated for any target repo you mutate manually)
|
|
42
|
+
- `aws` CLI v2 (only needed for `ralph-tail-logs`; tests use a stub. Note: `ralph-bootstrap-aws` and `ralph-sync-credential` use the AWS SDK directly and do not require the `aws` CLI to be installed.)
|
|
43
|
+
- [`bats-core`](https://github.com/bats-core/bats-core) for tests
|
|
44
|
+
|
|
45
|
+
On macOS:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
brew install yq jq gh bats-core awscli
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
On Debian/Ubuntu:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
sudo apt-get install bats jq gh
|
|
55
|
+
# yq via the Go release tarball or `snap install yq`
|
|
56
|
+
# awscli v2 via the official installer at https://aws.amazon.com/cli/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Running the tests
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
bats tests/
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
All tests must pass before opening a PR.
|
|
66
|
+
|
|
67
|
+
## Style
|
|
68
|
+
|
|
69
|
+
- Bash modules: `set -euo pipefail` only in scripts under `bin/`, never in
|
|
70
|
+
files under `lib/` (a sourced library must not change the caller's shell
|
|
71
|
+
options).
|
|
72
|
+
- Module-private helpers are named `tcs::__name`; public functions are
|
|
73
|
+
`tcs::name` (replace `tcs` with the module's prefix).
|
|
74
|
+
- Errors go to stderr via the module's `__err` helper and are prefixed with the
|
|
75
|
+
module name so users can tell where a failure came from.
|
|
76
|
+
- Exit codes are documented at the top of each module so callers can branch on
|
|
77
|
+
them.
|
|
78
|
+
|
|
79
|
+
## Adding a new module
|
|
80
|
+
|
|
81
|
+
1. Add `lib/<module>.sh` with a header comment that documents:
|
|
82
|
+
- what the module does in one paragraph
|
|
83
|
+
- the public function signatures
|
|
84
|
+
- the exit-code contract
|
|
85
|
+
- external dependencies
|
|
86
|
+
2. Add a `bin/` CLI wrapper if the module is useful from the command line.
|
|
87
|
+
3. Add bats tests under `tests/` and fixtures under `tests/fixtures/`.
|
|
88
|
+
4. Update `README.md` and the relevant doc under `docs/` if the module adds
|
|
89
|
+
user-visible surface area.
|
package/README.md
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
# ralph-harness
|
|
2
|
+
|
|
3
|
+
Throwaway-EC2 loop that picks one `ready-for-agent` GitHub issue, implements it
|
|
4
|
+
on a fresh branch, opens a PR, and incorporates one auto-review pass — then
|
|
5
|
+
terminates.
|
|
6
|
+
|
|
7
|
+
Generic. Target repo + target-specific config supplied at runtime.
|
|
8
|
+
|
|
9
|
+
Status: iteration 1, in progress (single-fire from laptop). See issue #1 for
|
|
10
|
+
the PRD and the open issues for the slice plan.
|
|
11
|
+
|
|
12
|
+
## Out of scope (iteration 1)
|
|
13
|
+
|
|
14
|
+
Iteration 1 is single-fire-from-laptop and intentionally minimal. The
|
|
15
|
+
following are deferred to later iterations:
|
|
16
|
+
|
|
17
|
+
- **Iteration 2 — management EC2.** A long-lived management box that
|
|
18
|
+
schedules `fire.sh` itself on a cron so the laptop stops being in the
|
|
19
|
+
loop. Reuses the slice 5 launcher and the slice 6 cloud-init module
|
|
20
|
+
unchanged.
|
|
21
|
+
- **Iteration 2 — Lambda janitor.** Out-of-band sweeper that
|
|
22
|
+
force-terminates any `Project=ralph` EC2 still running past
|
|
23
|
+
`MaxLifetimeMin`, defending against a launcher process killed before its
|
|
24
|
+
ceiling fires.
|
|
25
|
+
- **Iteration 3 — custom AMI.** Bake the dependency install (Node, .NET,
|
|
26
|
+
`gh`, Docker, `uv`, `claude`, `jq`, `yq`) so cold-start drops from minutes
|
|
27
|
+
to seconds. Iteration 1 installs at boot via `dotnet-install.sh` etc.
|
|
28
|
+
- **Iteration 3 — spot + ARM (`t4g`).** Iteration 1 fires
|
|
29
|
+
`m7a.xlarge` on-demand for predictability under the wall-clock backstop.
|
|
30
|
+
- **Iteration 3 — multi-target dispatch.** One run, one target. Routing
|
|
31
|
+
across multiple target repos is deferred.
|
|
32
|
+
- **Iteration 3 — observability dashboard.** Iteration 1 ships
|
|
33
|
+
per-instance CloudWatch streams plus the milestone-log GitHub issue
|
|
34
|
+
(caveman-format). A grafana/CW-dashboard view is later.
|
|
35
|
+
|
|
36
|
+
## Architecture: iteration-2-extensible
|
|
37
|
+
|
|
38
|
+
The slice 5 launcher (`lib/fire-launcher.sh`) and slice 6 cloud-init module
|
|
39
|
+
(`lib/cloud-init/bootstrap.sh`) are written so the iteration-2 management
|
|
40
|
+
EC2 can call `fire::run` exactly the way the laptop does today. No core
|
|
41
|
+
script needs a rewrite to move the trigger off the laptop — the management
|
|
42
|
+
box just needs the same env (`RALPH_TARGET_REPO`, IAM permissions for the
|
|
43
|
+
launcher) and a cron entry. See [`docs/smoke-test.md`](docs/smoke-test.md)
|
|
44
|
+
for the manual end-to-end runbook iteration 2 will automate.
|
|
45
|
+
|
|
46
|
+
## OAuth-vs-API-key migration footnote
|
|
47
|
+
|
|
48
|
+
Iteration 1 ships **OAuth-via-Keychain**. The EC2 worker authenticates to
|
|
49
|
+
Claude using the OAuth credential synced from the operator's macOS Keychain
|
|
50
|
+
into SSM (slice 4). Switching to a long-lived **API key** is a one-file
|
|
51
|
+
change in `lib/cloud-init/bootstrap.sh` (the `boot__fetch_secrets` step):
|
|
52
|
+
swap the SSM key, drop the credential file at the API-key consumer's
|
|
53
|
+
expected location instead. No other module changes. Tracked as iteration-2
|
|
54
|
+
work in issue #7.
|
|
55
|
+
|
|
56
|
+
OAuth caveats that apply today (and will go away if you migrate to an API
|
|
57
|
+
key):
|
|
58
|
+
|
|
59
|
+
- **Plan limits.** The EC2 worker burns the engineer's Claude plan limits
|
|
60
|
+
on every run.
|
|
61
|
+
- **Rotation.** Each desktop `claude /login` may invalidate the prior
|
|
62
|
+
refresh token. Re-run `ralph-sync-credential` immediately after every
|
|
63
|
+
login, or the worker will fail at the first `claude --print` call.
|
|
64
|
+
- **Concurrent use unverified.** Simultaneous use of the same OAuth
|
|
65
|
+
credential by the desktop app and an EC2 worker is not validated.
|
|
66
|
+
Assume one active consumer at a time.
|
|
67
|
+
|
|
68
|
+
## Public-safe
|
|
69
|
+
|
|
70
|
+
This repository contains zero target-specific identifiers. Every target knob is
|
|
71
|
+
supplied at runtime via the target repo's `.ralph/config.yaml` and via
|
|
72
|
+
environment variables / SSM. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for the
|
|
73
|
+
contract.
|
|
74
|
+
|
|
75
|
+
## What's here today
|
|
76
|
+
|
|
77
|
+
Slice 10 — end-to-end smoke-test runbook (HITL):
|
|
78
|
+
|
|
79
|
+
- [`docs/smoke-test.md`](docs/smoke-test.md) — operator-driven runbook
|
|
80
|
+
that exercises the full chain on a fresh AWS account: `bootstrap-aws` →
|
|
81
|
+
`sync-credential` → seed PAT → `fire` → tail CloudWatch → verify PR →
|
|
82
|
+
verify cleanup. Acceptance signal is "every phase reached `PHASE_END`
|
|
83
|
+
and the instance is `terminated`."
|
|
84
|
+
|
|
85
|
+
Slice 1 — target-config-schema:
|
|
86
|
+
|
|
87
|
+
- [`docs/config-schema.md`](docs/config-schema.md) — full schema for the
|
|
88
|
+
`.ralph/config.yaml` file every target repo must commit.
|
|
89
|
+
- [`lib/target-config-schema.sh`](lib/target-config-schema.sh) — sourceable
|
|
90
|
+
bash module that loads and validates a target repo's `.ralph/config.yaml`.
|
|
91
|
+
Fails loud with a useful message on any error.
|
|
92
|
+
- [`bin/load-config`](bin/load-config) — thin CLI over the validator. Exits 0
|
|
93
|
+
on valid input, non-zero on bad input.
|
|
94
|
+
|
|
95
|
+
Slice 2 — github-state-mutator:
|
|
96
|
+
|
|
97
|
+
- [`lib/github-state-mutator.sh`](lib/github-state-mutator.sh) — idempotent
|
|
98
|
+
shell wrappers around `gh` for the four state mutations the orchestrator
|
|
99
|
+
needs: `swap_label`, `comment_issue`,
|
|
100
|
+
`find_or_create_milestone_log_issue`, `append_caveman_log`.
|
|
101
|
+
- [`bin/gsm`](bin/gsm) — CLI for manual verification against a sandbox repo.
|
|
102
|
+
|
|
103
|
+
Slice 3 — aws-bootstrap (TS, slice-4 port):
|
|
104
|
+
|
|
105
|
+
- [`src/lib/aws-bootstrap.ts`](src/lib/aws-bootstrap.ts) — idempotent
|
|
106
|
+
`ensure*` functions for every AWS-side resource the harness needs (KMS
|
|
107
|
+
alias `alias/ralph`, SSM SecureString placeholders, EC2 IAM role +
|
|
108
|
+
instance profile with minimum-scope inline policy, security group in the
|
|
109
|
+
default VPC, CloudWatch log group) plus the target-side `agent-stuck`
|
|
110
|
+
label. Implemented over the AWS SDK v3 clients (`@aws-sdk/client-{kms,ssm,iam,ec2,cloudwatch-logs,sts}`).
|
|
111
|
+
- `ralph-bootstrap-aws` (`src/bin/ralph-bootstrap-aws.ts`) — single-shot
|
|
112
|
+
CLI: reads config from env, ensures every resource, second run is a
|
|
113
|
+
clean no-op. Region is forced to `eu-central-1`.
|
|
114
|
+
|
|
115
|
+
Slice 5 — fire-launcher (single-fire EC2 + CloudWatch streaming):
|
|
116
|
+
|
|
117
|
+
- [`lib/fire-launcher.sh`](lib/fire-launcher.sh) — fires one throwaway EC2
|
|
118
|
+
in `eu-central-1` (`m7a.xlarge`, 30 GB gp3, AL2023 from public SSM AMI
|
|
119
|
+
parameter, default-VPC public subnet, auto-assigned public IP, IMDSv2
|
|
120
|
+
required) using the bootstrapped IAM instance profile and security group.
|
|
121
|
+
Tags every instance + volume `Project=ralph` plus a UTC `LaunchedAt`
|
|
122
|
+
timestamp and `MaxLifetimeMin`. Launches with
|
|
123
|
+
`--instance-initiated-shutdown-behavior terminate`, then polls
|
|
124
|
+
`describe-instances` until `terminated`. On the 75-minute ceiling the
|
|
125
|
+
launcher force-`terminate-instances` and exits non-zero.
|
|
126
|
+
- [`bin/fire.sh`](bin/fire.sh) — single-shot launcher CLI.
|
|
127
|
+
|
|
128
|
+
Slice 9 — review call (post-PR) + post-hoc agent-stuck detection:
|
|
129
|
+
|
|
130
|
+
- [`prompts/review.md`](prompts/review.md) — generic review prompt
|
|
131
|
+
template. Target context (repo, default branch, work dir, build/test
|
|
132
|
+
commands, picked issue number, PR number, PR branch, configured
|
|
133
|
+
`review_bot.username` and `.source`, optional
|
|
134
|
+
`prompt_extensions.review`) is injected via `{{...}}` placeholders;
|
|
135
|
+
the template itself contains zero target-specific identifiers.
|
|
136
|
+
- `lib/ec2-orchestrator.sh` — `orch::run` now chains a review call
|
|
137
|
+
onto the `PR_OPENED` branch from slice 8: bash `sleep
|
|
138
|
+
RALPH_REVIEW_WAIT_SEC` (default `600`s — the configured external
|
|
139
|
+
review bot's window), render `prompts/review.md`, invoke
|
|
140
|
+
`claude --print`, wrap the call in
|
|
141
|
+
`PHASE_START phase=review issue=<n> pr=<m>` /
|
|
142
|
+
`PHASE_END phase=review duration_s=… status=…` markers (status
|
|
143
|
+
comes from the result file), verify `/tmp/ralph/review-result.json`,
|
|
144
|
+
branch on its `status`. Right after the discovery `PICKED` branch
|
|
145
|
+
the orchestrator emits a stable `PICKED_ISSUE=<n>` marker so the
|
|
146
|
+
launcher's post-hoc check can correlate hard-killed instances back
|
|
147
|
+
to a source issue.
|
|
148
|
+
- The review call writes `/tmp/ralph/review-result.json` with one of
|
|
149
|
+
two shapes:
|
|
150
|
+
- `{"status":"NO_REVIEW","reason":"…"}` — no comments / reviews
|
|
151
|
+
from the configured `review_bot.username` (with matching
|
|
152
|
+
`review_bot.source`) within the 10-minute window. No revision,
|
|
153
|
+
no caveman log entry, no state mutation.
|
|
154
|
+
- `{"status":"REVISION_APPLIED","issue":<n>,"pr_number":<m>,"summary":"…","gotcha":"…"}`
|
|
155
|
+
— exactly ONE revision pass: the call addresses the configured
|
|
156
|
+
bot's verdict, runs build + test until green, commits with a
|
|
157
|
+
`review: address …` message, and pushes to the same PR branch.
|
|
158
|
+
No follow-up rounds, no agent-vs-agent ping-pong.
|
|
159
|
+
- Bash branches on `review-result.json.status`: `NO_REVIEW` →
|
|
160
|
+
`OUTCOME=pr_opened issue=<n> pr=<m> review=none`; `REVISION_APPLIED`
|
|
161
|
+
→ `OUTCOME=pr_opened issue=<n> pr=<m> review=revised` and one
|
|
162
|
+
caveman-format comment is appended to the milestone-log issue via
|
|
163
|
+
`gsm::append_caveman_log` (from `lib/github-state-mutator.sh`,
|
|
164
|
+
bundled into the EC2 user-data alongside the orchestrator).
|
|
165
|
+
- Multi-reviewer case: only the configured `review_bot` is consulted;
|
|
166
|
+
Copilot, humans, and other bots on the same PR are ignored.
|
|
167
|
+
- `lib/fire-launcher.sh` — embeds `prompts/review.md` alongside
|
|
168
|
+
discovery + implementation, exports
|
|
169
|
+
`RALPH_REVIEW_PROMPT=/opt/ralph/prompts/review.md`, and bundles
|
|
170
|
+
`lib/github-state-mutator.sh` into the user-data so the orchestrator
|
|
171
|
+
can call `gsm::append_caveman_log` on the worker. After
|
|
172
|
+
`wait_for_terminated` (regardless of clean exit or wall-clock breach)
|
|
173
|
+
the launcher runs a post-hoc agent-stuck check from the laptop:
|
|
174
|
+
- List target-repo PRs whose body contains
|
|
175
|
+
`<!-- ralph-launch: <RALPH_LAUNCH_TAG> -->` (the marker the impl
|
|
176
|
+
call embeds). If at least one is found, the run is treated as a
|
|
177
|
+
clean termination.
|
|
178
|
+
- Otherwise, fetch the per-instance CloudWatch stream
|
|
179
|
+
(`aws logs filter-log-events`) for the orchestrator's
|
|
180
|
+
`PICKED_ISSUE=<n>` marker. If found, apply the configured
|
|
181
|
+
`agent_stuck_label` (default `agent-stuck`) to that source issue
|
|
182
|
+
via `gh issue edit`. Covers both wall-clock-killed and
|
|
183
|
+
orchestrator-crashed cases where the impl call could not
|
|
184
|
+
self-label.
|
|
185
|
+
- If neither a tagged PR nor a `PICKED_ISSUE` marker is recoverable,
|
|
186
|
+
the post-hoc check is a no-op (clean exit).
|
|
187
|
+
|
|
188
|
+
Slice 8 — implementation call:
|
|
189
|
+
|
|
190
|
+
- [`prompts/implementation.md`](prompts/implementation.md) — generic
|
|
191
|
+
implementation prompt template. Target context (repo, default branch,
|
|
192
|
+
work dir, build/test commands, branch prefix, agent-stuck label,
|
|
193
|
+
per-launch tag, optional `prompt_extensions.implementation`) is
|
|
194
|
+
injected via `{{...}}` placeholders at render time; the template
|
|
195
|
+
itself contains zero target-specific identifiers.
|
|
196
|
+
- `lib/ec2-orchestrator.sh` — `orch::run` now chains the implementation
|
|
197
|
+
call onto the `PICKED` branch from discovery: renders
|
|
198
|
+
`prompts/implementation.md`, appends the crafted context discovery
|
|
199
|
+
wrote to `/tmp/ralph/crafted-prompt.md`, invokes `claude --print`,
|
|
200
|
+
wraps the call in `PHASE_START phase=implementation` /
|
|
201
|
+
`PHASE_END phase=implementation duration_s=... issue=<n> status=...`
|
|
202
|
+
markers (status comes from the result file so a CloudWatch grep
|
|
203
|
+
yields a one-line per-iteration summary), and verifies the result
|
|
204
|
+
file before branching.
|
|
205
|
+
- The implementation call writes `/tmp/ralph/impl-result.json` with one
|
|
206
|
+
of two shapes:
|
|
207
|
+
- `{"status":"PR_OPENED","issue":<n>,"pr_number":<m>,"pr_url":...,"branch":...}`
|
|
208
|
+
- `{"status":"AGENT_STUCK","issue":<n>,"reason":"..."}`
|
|
209
|
+
- Bash branches on `impl-result.json.status`: `PR_OPENED` →
|
|
210
|
+
`OUTCOME=pr_opened issue=<n> pr=<m>`; `AGENT_STUCK` →
|
|
211
|
+
`OUTCOME=agent_stuck issue=<n>`. Any other status, missing output,
|
|
212
|
+
or invalid JSON aborts with exit 3 — the EC2 instance still
|
|
213
|
+
terminates via the cloud-init EXIT trap.
|
|
214
|
+
- The PR body carries an HTML-comment marker
|
|
215
|
+
`<!-- ralph-launch: <RALPH_LAUNCH_TAG> -->` (defaults to the EC2
|
|
216
|
+
instance id, set in `lib/cloud-init/bootstrap.sh`). Slice 9's
|
|
217
|
+
launcher post-hoc check uses this to correlate when the EC2 was
|
|
218
|
+
hard-killed before recording state.
|
|
219
|
+
- `agent-stuck` escape: the prompt instructs the impl call to
|
|
220
|
+
self-stop when ANY of (>3 build/test fix iterations on the same
|
|
221
|
+
failure surface) OR (>15 file edits without a green build) OR
|
|
222
|
+
(self-judged futility) hits. On stuck: the source issue gets the
|
|
223
|
+
`agent_stuck_label` label (default `agent-stuck`), no PR is opened,
|
|
224
|
+
and `impl-result.json` records `status=AGENT_STUCK`.
|
|
225
|
+
- The launcher embeds `prompts/implementation.md` into the rendered
|
|
226
|
+
user-data alongside the discovery prompt and exports
|
|
227
|
+
`RALPH_IMPLEMENTATION_PROMPT=/opt/ralph/prompts/implementation.md`.
|
|
228
|
+
|
|
229
|
+
Slice 7 — discovery call:
|
|
230
|
+
|
|
231
|
+
- [`prompts/discovery.md`](prompts/discovery.md) — generic discovery
|
|
232
|
+
prompt template. Target context (repo, default branch, work dir,
|
|
233
|
+
build/test commands, branch prefix, optional `prompt_extensions.discovery`)
|
|
234
|
+
is injected via `{{...}}` placeholders at render time; the template
|
|
235
|
+
itself contains zero target-specific identifiers.
|
|
236
|
+
- [`lib/ec2-orchestrator.sh`](lib/ec2-orchestrator.sh) — `orch::run`
|
|
237
|
+
now fires the real discovery call: renders the template, invokes
|
|
238
|
+
`claude --print` (with `--permission-mode bypassPermissions` by
|
|
239
|
+
default; override via `RALPH_CLAUDE_FLAGS`), wraps the call in
|
|
240
|
+
`PHASE_START phase=discovery` / `PHASE_END phase=discovery duration_s=...`
|
|
241
|
+
markers, and verifies the four output files exist before branching.
|
|
242
|
+
Each invocation is a fresh claude session (`memory` MCP excluded by
|
|
243
|
+
slice 6's MCP set).
|
|
244
|
+
- The discovery call writes four files under `$RALPH_OUT_DIR`
|
|
245
|
+
(default `/tmp/ralph/`):
|
|
246
|
+
- `decision.json` — `status` (`PICKED` | `NONE` | `ALL_BLOCKED`),
|
|
247
|
+
picked issue number, reasoning.
|
|
248
|
+
- `issue.json` — full `gh issue view --json ...` payload of the
|
|
249
|
+
picked issue (or `{}` for non-PICKED).
|
|
250
|
+
- `crafted-prompt.md` — implementation prompt for slice 8, with
|
|
251
|
+
target conventions surfaced from the target's `CLAUDE.md`,
|
|
252
|
+
`AGENTS.md`, `CONTEXT.md`, and any `docs/adr/*` files.
|
|
253
|
+
- `milestone-log.json` — `{milestone, log_issue}` pointing at the
|
|
254
|
+
`[log] <milestone>` issue (find-or-create) for cross-iteration
|
|
255
|
+
learnings.
|
|
256
|
+
- Bash branches on `decision.json.status`: `NONE` → `OUTCOME=no_work`,
|
|
257
|
+
`ALL_BLOCKED` → `OUTCOME=all_blocked`, `PICKED` → `OUTCOME=picked issue=<n>`
|
|
258
|
+
(slices 8/9 plug in here). Any other status, missing output file,
|
|
259
|
+
or invalid JSON aborts with exit 3 — the EC2 instance still
|
|
260
|
+
terminates via the cloud-init EXIT trap.
|
|
261
|
+
- The launcher embeds `prompts/discovery.md` into the rendered
|
|
262
|
+
user-data via a quoted heredoc and exports
|
|
263
|
+
`RALPH_DISCOVERY_PROMPT=/opt/ralph/prompts/discovery.md`, so the
|
|
264
|
+
prompt template ships alongside the lib bundle without a network
|
|
265
|
+
fetch.
|
|
266
|
+
|
|
267
|
+
Slice 6 — ec2-bootstrap (deps, secrets, fresh clone, hello orchestrator):
|
|
268
|
+
|
|
269
|
+
- [`lib/cloud-init/bootstrap.sh`](lib/cloud-init/bootstrap.sh) — slice 6's
|
|
270
|
+
cloud-init payload. Runs once on first boot. Installs OS dependencies
|
|
271
|
+
(Node 20, .NET 10 SDK via `dotnet-install.sh`, `gh`, Docker, `uv`,
|
|
272
|
+
`claude` CLI, plus `git`, `jq`, and `yq`). Configures the five MCPs the
|
|
273
|
+
harness uses (`serena`, `morph-mcp`, `context7`, `github`,
|
|
274
|
+
`sequential-thinking`); the `memory` MCP is intentionally NOT added so
|
|
275
|
+
every iteration starts with fresh context. Fetches the GitHub PAT and
|
|
276
|
+
the Claude OAuth credential from SSM SecureString
|
|
277
|
+
(`/ralph/github-pat`, `/ralph/claude-oauth-credential`) into mode-0600
|
|
278
|
+
files at their consumer's expected on-disk locations; never echoes the
|
|
279
|
+
values. Clones the target repo fresh on its resolved default branch
|
|
280
|
+
(`gh repo view --json defaultBranchRef`, no `main`/`master` assumption).
|
|
281
|
+
Runs safety guards (on default branch, clean working tree, origin
|
|
282
|
+
matches `RALPH_TARGET_REPO`). Validates `.ralph/config.yaml` via
|
|
283
|
+
`tcs::validate`. Hands off to `orch::run`. Ships stdout to CloudWatch
|
|
284
|
+
(`/ralph/main`, stream-per-instance) and runs `shutdown -h now` via a
|
|
285
|
+
bash `trap EXIT` so the box terminates on any exit (success or failure).
|
|
286
|
+
- `lib/ec2-orchestrator.sh` — sourceable shell module defining
|
|
287
|
+
`orch::run`. Slice 6 shipped a stub; slice 7 replaces it with the
|
|
288
|
+
real discovery call (see above). Slices 8/9 plug in implementation
|
|
289
|
+
and review after the `PICKED` branch.
|
|
290
|
+
- The launcher renders one self-contained user-data script by
|
|
291
|
+
concatenating `lib/target-config-schema.sh`, `lib/ec2-orchestrator.sh`,
|
|
292
|
+
and `lib/cloud-init/bootstrap.sh` after a small env shim that exports
|
|
293
|
+
the five required runtime knobs (`RALPH_TARGET_REPO`,
|
|
294
|
+
`RALPH_AWS_REGION`, `RALPH_GITHUB_TOKEN_SSM_KEY`,
|
|
295
|
+
`RALPH_CLAUDE_OAUTH_SSM_KEY`, `RALPH_LOG_GROUP`). No SSH at any layer;
|
|
296
|
+
SSM Session Manager is the only debug entry point.
|
|
297
|
+
|
|
298
|
+
```sh
|
|
299
|
+
# Fire one EC2 (slice 6 ec2-bootstrap, stub orchestrator):
|
|
300
|
+
RALPH_TARGET_REPO=owner/target ralph-fire
|
|
301
|
+
|
|
302
|
+
# Tail the per-instance CloudWatch stream:
|
|
303
|
+
aws --region eu-central-1 logs tail /ralph/main \
|
|
304
|
+
--log-stream-names <i-...> --follow
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Slice 4 — operator helper CLIs (TS, macOS-only Keychain syncer):
|
|
308
|
+
|
|
309
|
+
- [`src/lib/credential-syncer.ts`](src/lib/credential-syncer.ts) — reads
|
|
310
|
+
the `Claude Code-credentials` entry from the macOS Keychain and uploads
|
|
311
|
+
it to the SSM SecureString at `/ralph/claude-oauth-credential`
|
|
312
|
+
(overridable via `RALPH_CLAUDE_OAUTH_SSM_KEY`), encrypted under
|
|
313
|
+
`alias/ralph`.
|
|
314
|
+
- `ralph-sync-credential` (`src/bin/ralph-sync-credential.ts`) — thin CLI
|
|
315
|
+
wrapper. Region is forced to `eu-central-1`. The credential is passed
|
|
316
|
+
to `@aws-sdk/client-ssm` as the request body of `PutParameter`, so it
|
|
317
|
+
is never visible on any process's argv and is never echoed in info or
|
|
318
|
+
error output.
|
|
319
|
+
- `ralph-sync-github-pat` — net-new operator CLI: reads a GitHub PAT
|
|
320
|
+
from stdin and uploads it to the SSM SecureString at `/ralph/github-pat`
|
|
321
|
+
(overridable via `RALPH_GITHUB_TOKEN_SSM_KEY`). Pass the token on stdin
|
|
322
|
+
only — argv is rejected.
|
|
323
|
+
- `ralph-tail-logs` — thin wrapper around `aws logs tail` that defaults
|
|
324
|
+
to `/ralph/main` and the worker's per-instance stream.
|
|
325
|
+
|
|
326
|
+
Run after every desktop `claude /login`:
|
|
327
|
+
|
|
328
|
+
```sh
|
|
329
|
+
ralph-sync-credential
|
|
330
|
+
# or with a custom key:
|
|
331
|
+
RALPH_CLAUDE_OAUTH_SSM_KEY=/ralph/claude-oauth-credential ralph-sync-credential
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Caveats:
|
|
335
|
+
|
|
336
|
+
- **Rotation:** every desktop `claude /login` may invalidate the prior
|
|
337
|
+
refresh token. Re-run `ralph-sync-credential` immediately after each
|
|
338
|
+
login so the EC2 worker picks up the fresh credential.
|
|
339
|
+
- **Concurrent use unverified:** simultaneous use of the same credential by
|
|
340
|
+
the desktop app and an EC2 worker has not been validated; assume one
|
|
341
|
+
active consumer at a time.
|
|
342
|
+
- **Plan limits:** the EC2 worker burns the engineer's Claude plan limits
|
|
343
|
+
while running.
|
|
344
|
+
- **OAuth-vs-API-key:** iteration 1 ships OAuth-via-Keychain. Switching to a
|
|
345
|
+
long-lived API key is a one-file change in the future `ec2-bootstrap`
|
|
346
|
+
module (see issue #7).
|
|
347
|
+
|
|
348
|
+
[`src/`](src/) holds the TS modules and bin entries with co-located vitest
|
|
349
|
+
tests (`*.test.ts`). Run with `npm test`.
|
|
350
|
+
|
|
351
|
+
```sh
|
|
352
|
+
# Validate a config file by hand:
|
|
353
|
+
ralph-validate-config path/to/.ralph/config.yaml
|
|
354
|
+
|
|
355
|
+
# Manually swap a label on a sandbox repo:
|
|
356
|
+
ralph-gsm swap-label owner/sandbox 1 ready-for-agent ready-for-human
|
|
357
|
+
|
|
358
|
+
# Bootstrap AWS resources for a target repo (idempotent):
|
|
359
|
+
RALPH_TARGET_REPO=owner/target ralph-bootstrap-aws
|
|
360
|
+
|
|
361
|
+
# Sync the macOS Keychain credential into SSM (re-run after every claude /login):
|
|
362
|
+
ralph-sync-credential
|
|
363
|
+
|
|
364
|
+
# Seed the GitHub PAT into SSM (one-time):
|
|
365
|
+
echo "$GITHUB_PAT" | ralph-sync-github-pat
|
|
366
|
+
|
|
367
|
+
# Fire one throwaway EC2 instance (full discovery → impl → review chain):
|
|
368
|
+
RALPH_TARGET_REPO=owner/target ralph-fire
|
|
369
|
+
|
|
370
|
+
# Run the test suite:
|
|
371
|
+
npm test
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Dependencies (operator side): Node ≥ 24 and the harness installed globally
|
|
375
|
+
(`npm install -g @unimatrix27/ralph-harness@1.0.0`), plus `jq`, `gh`, and
|
|
376
|
+
`aws` CLI v2. See [`CONTRIBUTING.md`](CONTRIBUTING.md) for install hints.
|
|
377
|
+
|
|
378
|
+
## Schema at a glance
|
|
379
|
+
|
|
380
|
+
A target repo's `.ralph/config.yaml` looks like:
|
|
381
|
+
|
|
382
|
+
```yaml
|
|
383
|
+
build_cmd: "make build"
|
|
384
|
+
test_cmd: "make test"
|
|
385
|
+
branch_prefix: "ralph"
|
|
386
|
+
review_bot:
|
|
387
|
+
username: "claude"
|
|
388
|
+
source: "comment"
|
|
389
|
+
# optional:
|
|
390
|
+
agent_stuck_label: "agent-stuck"
|
|
391
|
+
prompt_extensions:
|
|
392
|
+
discovery: |
|
|
393
|
+
...extra discovery-prompt instructions...
|
|
394
|
+
implementation: |
|
|
395
|
+
...extra impl-prompt instructions...
|
|
396
|
+
review: |
|
|
397
|
+
...extra review-prompt instructions...
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
See [`docs/config-schema.md`](docs/config-schema.md) for the full spec, types,
|
|
401
|
+
and validation rules.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ralph-bootstrap-aws.d.ts","sourceRoot":"","sources":["../../src/bin/ralph-bootstrap-aws.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//
|
|
3
|
+
// ralph-bootstrap-aws — idempotent first-run bootstrap of the AWS-side and
|
|
4
|
+
// target-side resources the ralph-harness needs. Drop-in replacement for
|
|
5
|
+
// the (deleted) bin/bootstrap-aws.sh.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// ralph-bootstrap-aws
|
|
9
|
+
//
|
|
10
|
+
// Reads from env:
|
|
11
|
+
// RALPH_TARGET_REPO required (owner/repo)
|
|
12
|
+
// RALPH_GITHUB_TOKEN_SSM_KEY defaults to /ralph/github-pat
|
|
13
|
+
// RALPH_CLAUDE_OAUTH_SSM_KEY defaults to /ralph/claude-oauth-credential
|
|
14
|
+
// RALPH_LOG_GROUP defaults to /ralph/main
|
|
15
|
+
//
|
|
16
|
+
// Region is forced to eu-central-1 (matches lib/aws-clients.ts AWS_REGION).
|
|
17
|
+
//
|
|
18
|
+
// Re-running on an already-bootstrapped account is a clean no-op.
|
|
19
|
+
//
|
|
20
|
+
// Exit codes (matching bin/bootstrap-aws.sh):
|
|
21
|
+
// 0 success
|
|
22
|
+
// 2 usage / missing required env var
|
|
23
|
+
// non-zero propagated from underlying SDK / gh failure
|
|
24
|
+
import { DEFAULTS, moduleErr, runAll } from "../lib/aws-bootstrap.js";
|
|
25
|
+
import { defaultAwsClients } from "../lib/aws-clients.js";
|
|
26
|
+
async function main() {
|
|
27
|
+
const repo = process.env.RALPH_TARGET_REPO;
|
|
28
|
+
if (!repo || repo.length === 0) {
|
|
29
|
+
process.stderr.write(moduleErr("RALPH_TARGET_REPO is required (e.g. owner/repo)") + "\n");
|
|
30
|
+
process.exit(2);
|
|
31
|
+
}
|
|
32
|
+
const githubKey = process.env.RALPH_GITHUB_TOKEN_SSM_KEY || DEFAULTS.githubKey;
|
|
33
|
+
const oauthKey = process.env.RALPH_CLAUDE_OAUTH_SSM_KEY || DEFAULTS.oauthKey;
|
|
34
|
+
const logGroup = process.env.RALPH_LOG_GROUP || DEFAULTS.logGroup;
|
|
35
|
+
const clients = defaultAwsClients();
|
|
36
|
+
await runAll({ clients, repo, githubKey, oauthKey, logGroup });
|
|
37
|
+
}
|
|
38
|
+
main().catch((err) => {
|
|
39
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
40
|
+
process.stderr.write(`${msg}\n`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=ralph-bootstrap-aws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ralph-bootstrap-aws.js","sourceRoot":"","sources":["../../src/bin/ralph-bootstrap-aws.ts"],"names":[],"mappings":";AACA,EAAE;AACF,2EAA2E;AAC3E,yEAAyE;AACzE,sCAAsC;AACtC,EAAE;AACF,SAAS;AACT,wBAAwB;AACxB,EAAE;AACF,kBAAkB;AAClB,yDAAyD;AACzD,iEAAiE;AACjE,8EAA8E;AAC9E,2DAA2D;AAC3D,EAAE;AACF,4EAA4E;AAC5E,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,8CAA8C;AAC9C,gBAAgB;AAChB,yCAAyC;AACzC,2DAA2D;AAE3D,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC3C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,SAAS,CAAC,iDAAiD,CAAC,GAAG,IAAI,CACpE,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,SAAS,GACb,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,QAAQ,CAAC,SAAS,CAAC;IAC/D,MAAM,QAAQ,GACZ,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,QAAQ,CAAC,QAAQ,CAAC;IAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,QAAQ,CAAC,QAAQ,CAAC;IAElE,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,MAAM,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACjC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ralph-fire.d.ts","sourceRoot":"","sources":["../../src/bin/ralph-fire.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
//
|
|
3
|
+
// ralph-fire — slice 5 launcher (real). Replaces iteration-1's
|
|
4
|
+
// `bin/fire.sh` + `lib/fire-launcher.sh`. Loads `.env` from CWD then XDG,
|
|
5
|
+
// optionally subprocesses `ralph-bootstrap-aws` for first-run idempotent
|
|
6
|
+
// AWS-side resource ensure, then calls the launcher.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// ralph-fire — fire one EC2 worker
|
|
10
|
+
// ralph-fire --version — print the package version and exit
|
|
11
|
+
//
|
|
12
|
+
// Env knobs: see lib/fire-launcher.ts (resolveLauncherConfig). The set is
|
|
13
|
+
// byte-compatible with iteration-1's lib/fire-launcher.sh.
|
|
14
|
+
//
|
|
15
|
+
// Auto-bootstrap: defaults to subprocessing `ralph-bootstrap-aws` once
|
|
16
|
+
// before launching. Set RALPH_SKIP_BOOTSTRAP=1 to suppress.
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { createRequire } from "node:module";
|
|
19
|
+
import { loadDotenv } from "../lib/env-loader.js";
|
|
20
|
+
import { LauncherError, run } from "../lib/fire-launcher.js";
|
|
21
|
+
const require = createRequire(import.meta.url);
|
|
22
|
+
const pkg = require("../../package.json");
|
|
23
|
+
async function main() {
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
if (args.length === 1 && args[0] === "--version") {
|
|
26
|
+
process.stdout.write(`${pkg.version}\n`);
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
if (args.length > 0) {
|
|
30
|
+
process.stderr.write(`ralph-fire: unexpected arguments: ${args.join(" ")}\n`);
|
|
31
|
+
process.exit(2);
|
|
32
|
+
}
|
|
33
|
+
loadDotenv();
|
|
34
|
+
if (process.env.RALPH_SKIP_BOOTSTRAP !== "1") {
|
|
35
|
+
const r = spawnSync("ralph-bootstrap-aws", [], {
|
|
36
|
+
stdio: "inherit",
|
|
37
|
+
env: process.env,
|
|
38
|
+
});
|
|
39
|
+
if (r.error) {
|
|
40
|
+
process.stderr.write(`ralph-fire: ralph-bootstrap-aws not found on PATH; install the package globally (npm install -g @unimatrix27/ralph-harness) or set RALPH_SKIP_BOOTSTRAP=1\n`);
|
|
41
|
+
process.exit(2);
|
|
42
|
+
}
|
|
43
|
+
if ((r.status ?? 1) !== 0) {
|
|
44
|
+
process.exit(r.status ?? 1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const rc = await run({ defaultHarnessVersion: pkg.version });
|
|
48
|
+
process.exit(rc);
|
|
49
|
+
}
|
|
50
|
+
main().catch((err) => {
|
|
51
|
+
if (err instanceof LauncherError) {
|
|
52
|
+
process.stderr.write(`${err.message}\n`);
|
|
53
|
+
process.exit(err.exitCode);
|
|
54
|
+
}
|
|
55
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
56
|
+
process.stderr.write(`ralph-fire: ${msg}\n`);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=ralph-fire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ralph-fire.js","sourceRoot":"","sources":["../../src/bin/ralph-fire.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ralph-gsm.d.ts","sourceRoot":"","sources":["../../src/bin/ralph-gsm.ts"],"names":[],"mappings":""}
|