@osfactory/har 0.6.0 → 0.8.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/README.md +2 -0
- package/dist/index.js +1811 -648
- package/dist/prompts/system-authoring.md +27 -2
- package/dist/templates/adaptation-prompt-init.md +32 -2
- package/dist/templates/adaptation-prompt-maintain.md +5 -2
- package/dist/templates/har-boilerplate/README.md +39 -3
- package/dist/templates/har-boilerplate/agent-cli.sh +72 -26
- package/dist/templates/har-boilerplate/agent-slot.sh +351 -0
- package/dist/templates/har-boilerplate/attach.sh +7 -1
- package/dist/templates/har-boilerplate/ecosystem.agent.template.cjs +1 -1
- package/dist/templates/har-boilerplate/env.template +1 -1
- package/dist/templates/har-boilerplate/harness.env +27 -0
- package/dist/templates/har-boilerplate/launch.sh +167 -114
- package/dist/templates/har-boilerplate/preflight.sh +41 -0
- package/dist/templates/har-boilerplate/setup-infra.sh +87 -11
- package/dist/templates/har-boilerplate/teardown.sh +2 -1
- package/dist/templates/har-boilerplate-cli/README.md +28 -0
- package/dist/templates/har-boilerplate-cli/agent-slot.sh +331 -0
- package/dist/templates/har-boilerplate-cli/harness.env +7 -0
- package/dist/templates/har-boilerplate-cli/launch.sh +145 -95
- package/dist/templates/har-boilerplate-cli/preflight.sh +37 -0
- package/dist/templates/har-boilerplate-cli/setup-infra.sh +87 -11
- package/dist/templates/har-boilerplate-ios/README.md +26 -0
- package/dist/templates/har-boilerplate-ios/harness.env +2 -1
- package/package.json +1 -1
- package/dist/templates/har-boilerplate-ios/ADAPT-PROMPT.md +0 -57
|
@@ -104,12 +104,36 @@ If the repository contains more than one project or `.har` harness (check for `.
|
|
|
104
104
|
- Keep a small `AGENT.md` / `CLAUDE.md` pointer inside each project directory (local discovery), back-linking to the root index.
|
|
105
105
|
- One Cursor rule at repo root listing all harnesses — never one rule per project.
|
|
106
106
|
|
|
107
|
-
## Port allocation
|
|
107
|
+
## Port allocation & shared services
|
|
108
108
|
|
|
109
|
-
|
|
109
|
+
Document ports in `.har/README.md` and configure them in `.har/harness.env`. Resolved values land in `.env.agent.<id>` and `.har/slots/agent-<id>.json` at launch — never hardcode defaults in app code or tests.
|
|
110
|
+
|
|
111
|
+
### Per-slot app ports (default / PM2 profile)
|
|
112
|
+
|
|
113
|
+
| Layer | Scope | Rule | On conflict |
|
|
114
|
+
|-------|-------|------|-------------|
|
|
115
|
+
| Frontend | Per slot | `HARNESS_FE_BASE_PORT + (AGENT_ID × HARNESS_PORT_STEP)` | Scan `STEP` increments within the slot lane |
|
|
116
|
+
| API | Per slot | `HARNESS_API_BASE_PORT + (AGENT_ID × STEP)` | Same scan policy |
|
|
117
|
+
| Node debug | Per slot | `9200 + (AGENT_ID × STEP)` | Same scan policy |
|
|
118
|
+
|
|
119
|
+
### Shared infra ports (when listed in `HARNESS_INFRA_SERVICES`)
|
|
120
|
+
|
|
121
|
+
| Service | Default var | On conflict |
|
|
122
|
+
|---------|-------------|-------------|
|
|
123
|
+
| Postgres | `HARNESS_DB_PORT_DEFAULT` | Scan `HARNESS_DB_PORT_SCAN_START..END` |
|
|
124
|
+
| MinIO | `HARNESS_MINIO_PORT_DEFAULT` (+ console) | Scan configured ranges in `harness.env` |
|
|
125
|
+
| Mailpit | `HARNESS_MAILPIT_*_PORT_DEFAULT` | Scan configured ranges |
|
|
126
|
+
| Headless browser | `HARNESS_BROWSER_PORT_DEFAULT` | Scan configured ranges |
|
|
127
|
+
|
|
128
|
+
CLI and iOS profiles typically have no per-slot app ports; optional backends still use the shared-infra model.
|
|
110
129
|
|
|
111
130
|
Set slot limits in `.har/stages.json` (`agentSlots`) and `.har/harness.env` (`HARNESS_AGENT_SLOT_MIN` / `HARNESS_AGENT_SLOT_MAX`) based on machine capacity.
|
|
112
131
|
|
|
132
|
+
### Do not
|
|
133
|
+
|
|
134
|
+
- Hardcode `3000`, `15432`, `3847`, or other defaults in application code, tests, or agent docs
|
|
135
|
+
- Run raw `docker compose` for harness infrastructure — use `setup-infra.sh` / `launch.sh`
|
|
136
|
+
|
|
113
137
|
## Rules
|
|
114
138
|
|
|
115
139
|
1. **Edit .har/ files directly** — no YAML runtime config
|
|
@@ -120,6 +144,7 @@ Set slot limits in `.har/stages.json` (`agentSlots`) and `.har/harness.env` (`HA
|
|
|
120
144
|
6. **Do not edit manifest.json** — managed by har CLI
|
|
121
145
|
7. **Run the cleanup checklist before finishing** — keep strictly what this repository needs:
|
|
122
146
|
- compose file has only used services; `HARNESS_INFRA_SERVICES` matches exactly
|
|
147
|
+
- `harness.env` has port documentation vars for every enabled infra service; `.har/README.md` explains allocation and shared vs per-slot
|
|
123
148
|
- `env.template` has no blocks for removed services; no dead branches left in scripts
|
|
124
149
|
- unused harness files deleted (`deleteHarnessFile`), e.g. `attach.sh` when unused
|
|
125
150
|
- `.har/README.md` file table matches the files that actually exist
|
|
@@ -132,10 +132,38 @@ Detailed agent instructions: commands, credentials, architecture, definition of
|
|
|
132
132
|
### `.har/env.template`, `setup-infra.sh`, `docker-compose.agent.yml`
|
|
133
133
|
Adapt as needed for the project's infra.
|
|
134
134
|
|
|
135
|
-
### Port allocation
|
|
136
|
-
|
|
135
|
+
### Port allocation & shared services
|
|
136
|
+
|
|
137
|
+
Document and configure ports in `.har/harness.env` and `.har/README.md`. Use the bundled template's port-allocation block as the contract — do not hardcode ports in app code or tests.
|
|
138
|
+
|
|
139
|
+
**Per-slot app ports** (default / PM2 profile only):
|
|
140
|
+
|
|
141
|
+
| Layer | Scope | Rule | On conflict |
|
|
142
|
+
|-------|-------|------|-------------|
|
|
143
|
+
| Frontend | Per slot | `HARNESS_FE_BASE_PORT + (AGENT_ID × HARNESS_PORT_STEP)` | Scan `STEP` increments within the slot lane |
|
|
144
|
+
| API | Per slot | `HARNESS_API_BASE_PORT + (AGENT_ID × STEP)` | Same scan policy |
|
|
145
|
+
| Node debug | Per slot | `9200 + (AGENT_ID × STEP)` | Same scan policy |
|
|
146
|
+
|
|
147
|
+
**Shared infra ports** (one per machine, all profiles when the service is in `HARNESS_INFRA_SERVICES`):
|
|
148
|
+
|
|
149
|
+
| Service | Default var | On conflict |
|
|
150
|
+
|---------|-------------|-------------|
|
|
151
|
+
| Postgres | `HARNESS_DB_PORT_DEFAULT` | Scan `HARNESS_DB_PORT_SCAN_START..END` |
|
|
152
|
+
| MinIO | `HARNESS_MINIO_PORT_DEFAULT` (+ console) | Scan configured ranges |
|
|
153
|
+
| Mailpit | `HARNESS_MAILPIT_*_PORT_DEFAULT` | Scan configured ranges |
|
|
154
|
+
| Headless browser | `HARNESS_BROWSER_PORT_DEFAULT` | Scan configured ranges |
|
|
155
|
+
|
|
137
156
|
Set slot limits in `.har/stages.json` (`agentSlots`) and `.har/harness.env` (`HARNESS_AGENT_SLOT_MIN` / `HARNESS_AGENT_SLOT_MAX`) based on machine capacity.
|
|
138
157
|
|
|
158
|
+
**Port / infra checklist:**
|
|
159
|
+
|
|
160
|
+
- [ ] `.har/harness.env` has `HARNESS_FE_BASE_PORT`, `HARNESS_API_BASE_PORT`, `HARNESS_PORT_STEP` (default profile) or explains why they are absent (CLI/iOS)
|
|
161
|
+
- [ ] For each service in `HARNESS_INFRA_SERVICES`, matching `HARNESS_*_PORT_DEFAULT` and `SCAN_*` vars exist in `harness.env`
|
|
162
|
+
- [ ] `.har/README.md` has a **Port & shared services** section (allocation table, shared vs per-slot, do-not rules)
|
|
163
|
+
- [ ] App code and tests read ports from `.env.agent.<id>` / `agent-cli.sh` / slot registry — no hardcoded `3000`, `15432`, `3847`, etc.
|
|
164
|
+
- [ ] `env.template` and `CLAUDE.agent.md` show resolved ports via variables, not literals
|
|
165
|
+
- [ ] Monorepos with `har control up`: document slot-1 conflict if the app port overlaps (e.g. Mission Control on 3847)
|
|
166
|
+
|
|
139
167
|
### Git worktree
|
|
140
168
|
`launch.sh` creates an isolated session worktree at `~/worktrees/<base>-<sha4>-har-agent-<id>-<rand4>` by default (`HARNESS_USE_WORKTREE=true`) and records it in `.har/slots/agent-<id>.json`. Agents should commit from that worktree, not the main checkout.
|
|
141
169
|
|
|
@@ -187,6 +215,8 @@ The boilerplate ships more than any single repository needs. Strip it down to st
|
|
|
187
215
|
- [ ] If full seed/setup is skipped, `.har/` provides a minimal bootstrap or clearly documents why none is needed
|
|
188
216
|
- [ ] All per-slot data stores are provisioned, not just the primary database
|
|
189
217
|
- [ ] `.har/CLAUDE.agent.md` defines what "agent usable" means for this repo: login/API smoke, credentials, required default data, and known skipped dev-setup steps
|
|
218
|
+
- [ ] `.har/README.md` documents port allocation and shared-service model; `harness.env` has the port knobs for every enabled infra service
|
|
219
|
+
- [ ] No hardcoded default ports (`3000`, `15432`, `3847`, …) in app code, tests, or harness docs — use agent env / slot registry
|
|
190
220
|
|
|
191
221
|
## Rules
|
|
192
222
|
|
|
@@ -6,6 +6,8 @@ The harness already exists. Inspect what changed in the repo since the harness w
|
|
|
6
6
|
|
|
7
7
|
**Do NOT** create a YAML config or JSON mapping file for runtime behavior. Put behavior directly in the harness scripts and templates.
|
|
8
8
|
|
|
9
|
+
{{MAINTAIN_BUNDLE_SECTION}}
|
|
10
|
+
|
|
9
11
|
## Step 1 — Inspect the repository
|
|
10
12
|
|
|
11
13
|
Compare the current repo against the existing harness:
|
|
@@ -13,7 +15,7 @@ Compare the current repo against the existing harness:
|
|
|
13
15
|
- Root manifests, CI, Docker, README
|
|
14
16
|
- New or changed test, lint, build, migrate, or seed commands
|
|
15
17
|
- New services, ports, or environment variables
|
|
16
|
-
-
|
|
18
|
+
- Review `.har/maintain/drift-report.json` (generator version, template drift, **missing port documentation vars**)
|
|
17
19
|
|
|
18
20
|
## Step 2 — Update `.har/` files
|
|
19
21
|
|
|
@@ -64,9 +66,10 @@ When upgrading `@osfactory/har` or adopting new harness standards:
|
|
|
64
66
|
|
|
65
67
|
**Generator 0.4.0 — primary app & shared infra services:**
|
|
66
68
|
|
|
67
|
-
- Migrate `harness.env` from boolean `HARNESS_INFRA_*` flags to the `HARNESS_INFRA_SERVICES` list (space-separated compose service names, e.g. `"db mailpit"`) and add the `har_infra_enabled()` helper — copy both from
|
|
69
|
+
- Migrate `harness.env` from boolean `HARNESS_INFRA_*` flags to the `HARNESS_INFRA_SERVICES` list (space-separated compose service names, e.g. `"db mailpit"`) and add the `har_infra_enabled()` helper — copy both from `.har/maintain/templates/harness.env`. Update every script that still tests `HARNESS_INFRA_POSTGRES`-style flags (`setup-infra.sh`, `launch.sh`, `teardown.sh`, `agent-cli.sh`) to use `har_infra_enabled <service>`.
|
|
68
70
|
- Set `HARNESS_PRIMARY_APP` in `harness.env` to the ONE app agents modify. Ensure `ecosystem.agent.template.cjs` starts only that app's processes. Move any other in-repo services agents depend on but don't change to shared infra: compose services in `docker-compose.agent.yml` or an optional `.har/ecosystem.shared.config.cjs` (processes `har-shared-<name>`; `setup-infra.sh` starts it when present — resync `setup-infra.sh` from the template to get this hook).
|
|
69
71
|
- Prune `docker-compose.agent.yml` to only the services this project uses; delete unused menu services and volumes.
|
|
72
|
+
- **Port & shared services:** ensure `.har/README.md` documents the allocation table and shared vs per-slot model; `harness.env` has every `HARNESS_*_PORT_*` var required for services in `HARNESS_INFRA_SERVICES` (copy missing vars from `.har/maintain/templates/harness.env` when drift reports them). Remove hardcoded ports from app code, tests, and `CLAUDE.agent.md`.
|
|
70
73
|
- Run the **cleanup checklist**: no TODO placeholders, no env blocks for removed services in `env.template`, no dead script branches, `.har/README.md` file table matches the actual files, `CLAUDE.agent.md` shows only real URLs/ports and commands, unused files deleted.
|
|
71
74
|
|
|
72
75
|
**Earlier standards:**
|
|
@@ -113,14 +113,48 @@ Always use `./.har/agent-cli.sh <id> ...` — never hardcoded ports.
|
|
|
113
113
|
|
|
114
114
|
## Architecture
|
|
115
115
|
|
|
116
|
-
Each agent slot gets isolated ports
|
|
116
|
+
Each agent slot gets isolated app ports. Defaults follow `BASE + (AGENT_ID × HARNESS_PORT_STEP)`; when a default is busy, `launch.sh` scans the slot lane (`STEP` increments) and writes the resolved ports to `.env.agent.<id>` and `.har/slots/agent-<id>.json`.
|
|
117
117
|
|
|
118
118
|
Configure how many slots your machine can run in parallel in `stages.json` (`agentSlots`) and `harness.env` (`HARNESS_AGENT_SLOT_MIN` / `HARNESS_AGENT_SLOT_MAX`). Keep both in sync.
|
|
119
119
|
|
|
120
|
-
| Service | Agent 1 | Agent 2 |
|
|
121
|
-
|
|
120
|
+
| Service | Agent 1 (default) | Agent 2 (default) |
|
|
121
|
+
|---------|-------------------|-------------------|
|
|
122
122
|
| Frontend | 3010 | 3020 |
|
|
123
123
|
| API | 8010 | 8020 |
|
|
124
|
+
| Node debug | 9210 | 9220 |
|
|
125
|
+
|
|
126
|
+
## Port & shared services
|
|
127
|
+
|
|
128
|
+
### Port allocation
|
|
129
|
+
|
|
130
|
+
| Layer | Scope | Rule | On conflict |
|
|
131
|
+
|-------|-------|------|-------------|
|
|
132
|
+
| App — frontend | Per slot | `HARNESS_FE_BASE_PORT + (AGENT_ID × HARNESS_PORT_STEP)` | Scan `STEP` increments within the slot lane |
|
|
133
|
+
| App — API | Per slot | `HARNESS_API_BASE_PORT + (AGENT_ID × STEP)` | Same scan policy |
|
|
134
|
+
| Node debug | Per slot | `9200 + (AGENT_ID × STEP)` | Same scan policy |
|
|
135
|
+
| Shared Postgres | Per machine | `HARNESS_DB_PORT_DEFAULT` | Scan `HARNESS_DB_PORT_SCAN_START..END` |
|
|
136
|
+
| MinIO / S3 | Per machine | `HARNESS_MINIO_PORT_DEFAULT` (+ console port) | Scan configured ranges in `harness.env` |
|
|
137
|
+
| Mailpit | Per machine | `HARNESS_MAILPIT_*_PORT_DEFAULT` | Scan configured ranges |
|
|
138
|
+
| Headless browser | Per machine | `HARNESS_BROWSER_PORT_DEFAULT` | Scan configured ranges |
|
|
139
|
+
|
|
140
|
+
Resolved ports may differ from the formula when something else is already bound. Always use `./.har/agent-cli.sh <id>` or read `.har/slots/agent-<id>.json` — never hardcode `3010`, `15432`, etc. in app code or tests.
|
|
141
|
+
|
|
142
|
+
### Shared vs per-slot
|
|
143
|
+
|
|
144
|
+
| Resource | Model | Configuration |
|
|
145
|
+
|----------|-------|---------------|
|
|
146
|
+
| Postgres | One shared container; per-slot database `agent_<id>` cloned from template | `HARNESS_INFRA_SERVICES="db"` |
|
|
147
|
+
| MinIO / S3 | One shared container; per-slot bucket `agent-<id>` | `HARNESS_INFRA_SERVICES="... minio"` |
|
|
148
|
+
| Mailpit, Redis, etc. | One shared container on a scanned host port | Listed in `HARNESS_INFRA_SERVICES` |
|
|
149
|
+
| Primary application | One PM2 ecosystem per slot (isolated ports) | `HARNESS_PRIMARY_APP`, `ecosystem.agent.template.cjs` |
|
|
150
|
+
| Internal supporting services | Shared across all slots | `docker-compose.agent.yml` or `ecosystem.shared.config.cjs` |
|
|
151
|
+
|
|
152
|
+
Shared infra starts once via `./.har/setup-infra.sh` (also run automatically by `launch.sh`). Per-slot databases are cloned in `launch.sh`.
|
|
153
|
+
|
|
154
|
+
### Do not
|
|
155
|
+
|
|
156
|
+
- Hardcode default ports (`3000`, `15432`, `3847`, …) in application code, tests, or agent docs — read from `.env.agent.<id>`, `agent-cli.sh`, or the slot registry
|
|
157
|
+
- Run raw `docker compose` for harness infrastructure — use `setup-infra.sh` / `launch.sh` so ports are scanned and persisted in `.har/state/infra.env`
|
|
124
158
|
|
|
125
159
|
### Primary app vs shared services
|
|
126
160
|
|
|
@@ -156,6 +190,8 @@ work dir printed by launch, never in the main checkout.
|
|
|
156
190
|
after explicit user approval.
|
|
157
191
|
- `teardown` removes the worktree but **keeps the session branch** so you can push it
|
|
158
192
|
or open a PR (`--delete-branch` to drop it).
|
|
193
|
+
- If launch fails after creating a worktree/env file, the registry records `status: failed`.
|
|
194
|
+
Resume without `--replace`: `har env launch <id> --resume` or `har env recover <id>`.
|
|
159
195
|
- `har env complete <id>` finishes a session: full verify (recorded as a validation),
|
|
160
196
|
then teardown — branch kept.
|
|
161
197
|
- `--no-worktree` runs the slot from the repo root instead (single-agent mode).
|
|
@@ -17,51 +17,94 @@ COMMAND="${2:?Usage: agent-cli.sh <agent-id> <command> [args...]}"
|
|
|
17
17
|
|
|
18
18
|
validate_agent_id "$AGENT_ID"
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
DB_PORT="${AGENT_DB_PORT:-15432}"
|
|
20
|
+
load_agent_ports "$AGENT_ID" "$REPO_ROOT"
|
|
21
|
+
DB_PORT="${DB_PORT:-${AGENT_DB_PORT:-${HARNESS_DB_PORT_DEFAULT:-15432}}}"
|
|
23
22
|
export PGPASSWORD="password"
|
|
24
23
|
|
|
24
|
+
PM2_SLOT_PREFIX="$(har_pm2_slot_prefix "$AGENT_ID")"
|
|
25
|
+
|
|
25
26
|
case "$COMMAND" in
|
|
26
27
|
status)
|
|
27
28
|
ENV_FILE="$(resolve_agent_env_file "$AGENT_ID" "$REPO_ROOT" || true)"
|
|
28
29
|
WORKTREE_DIR="$(existing_slot_worktree "$AGENT_ID")"
|
|
29
|
-
|
|
30
|
+
REG_FILE="$(slot_registry_file "$AGENT_ID")"
|
|
30
31
|
PM2_RAW=$(npx --yes pm2 jlist 2>/dev/null || true)
|
|
31
32
|
PM2_FOUND=false
|
|
33
|
+
PM2_FOREIGN=false
|
|
34
|
+
PM2_LEGACY=false
|
|
32
35
|
|
|
33
36
|
if [ -n "$PM2_RAW" ]; then
|
|
37
|
+
set +e
|
|
34
38
|
echo "$PM2_RAW" | node -e "
|
|
35
39
|
const agentId = '${AGENT_ID}';
|
|
40
|
+
const project = '${HARNESS_PROJECT_NAME}';
|
|
41
|
+
const slotPrefix = 'har-' + project + '-agent-' + agentId + '-';
|
|
42
|
+
const legacyPrefix = 'agent-' + agentId + '-';
|
|
36
43
|
let raw = '';
|
|
37
44
|
process.stdin.on('data', c => raw += c);
|
|
38
45
|
process.stdin.on('end', () => {
|
|
39
46
|
try {
|
|
40
47
|
const arr = JSON.parse(raw);
|
|
41
48
|
if (!Array.isArray(arr)) process.exit(1);
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
const owned = arr.filter(x => x.name && x.name.startsWith(slotPrefix));
|
|
50
|
+
const foreignProject = arr.filter(x =>
|
|
51
|
+
x.name && x.name.startsWith('har-') && x.name.includes('-agent-' + agentId + '-') && !x.name.startsWith(slotPrefix));
|
|
52
|
+
const legacy = arr.filter(x => x.name && x.name.startsWith(legacyPrefix) && !x.name.startsWith('har-'));
|
|
53
|
+
if (owned.length > 0) {
|
|
54
|
+
console.log('Agent ' + agentId + ' processes (' + project + '):');
|
|
55
|
+
owned.forEach(p => {
|
|
56
|
+
const s = (p.pm2_env?.status || 'unknown').padEnd(10);
|
|
57
|
+
const mem = Math.round((p.monit?.memory || 0) / 1024 / 1024) + 'MB';
|
|
58
|
+
const cpu = (p.monit?.cpu || 0) + '%';
|
|
59
|
+
const cwd = p.pm2_env?.pm_cwd || p.pm2_env?.cwd || '';
|
|
60
|
+
console.log(' ' + p.name.padEnd(48) + s + ' mem=' + mem + ' cpu=' + cpu);
|
|
61
|
+
if (cwd) console.log(' cwd: ' + cwd);
|
|
62
|
+
});
|
|
63
|
+
process.exit(0);
|
|
64
|
+
}
|
|
65
|
+
if (foreignProject.length > 0 || legacy.length > 0) {
|
|
66
|
+
if (foreignProject.length > 0) {
|
|
67
|
+
console.log('ERROR: foreign PM2 processes match agent ' + agentId + ' but belong to another harness:');
|
|
68
|
+
foreignProject.forEach(p => console.log(' ' + p.name + ' cwd=' + (p.pm2_env?.pm_cwd || p.pm2_env?.cwd || 'unknown')));
|
|
69
|
+
}
|
|
70
|
+
if (legacy.length > 0) {
|
|
71
|
+
console.log('ERROR: legacy PM2 processes (pre project-scoping) match agent ' + agentId + ':');
|
|
72
|
+
legacy.forEach(p => console.log(' ' + p.name + ' cwd=' + (p.pm2_env?.pm_cwd || p.pm2_env?.cwd || 'unknown')));
|
|
73
|
+
}
|
|
74
|
+
process.exit(2);
|
|
75
|
+
}
|
|
76
|
+
process.exit(1);
|
|
51
77
|
} catch {
|
|
52
78
|
process.exit(1);
|
|
53
79
|
}
|
|
54
80
|
});
|
|
55
|
-
"
|
|
81
|
+
"
|
|
82
|
+
pm2_status=$?
|
|
83
|
+
set -e
|
|
84
|
+
case "$pm2_status" in
|
|
85
|
+
0) PM2_FOUND=true ;;
|
|
86
|
+
2) PM2_FOREIGN=true ;;
|
|
87
|
+
esac
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if [ "$PM2_FOREIGN" = true ]; then
|
|
91
|
+
exit 1
|
|
56
92
|
fi
|
|
57
93
|
|
|
58
94
|
if [ "$PM2_FOUND" = true ]; then
|
|
59
|
-
REG_FILE="$(slot_registry_file "$AGENT_ID")"
|
|
60
95
|
if [ ! -f "$REG_FILE" ]; then
|
|
61
|
-
echo "
|
|
96
|
+
echo "ERROR: slot registry missing at $REG_FILE but PM2 processes exist for this project." >&2
|
|
97
|
+
echo " Another harness may have been torn down incorrectly, or this slot was started outside launch.sh." >&2
|
|
98
|
+
exit 1
|
|
99
|
+
fi
|
|
100
|
+
REG_PROJECT="$(read_slot_field "$REG_FILE" projectName || true)"
|
|
101
|
+
if [ -n "$REG_PROJECT" ] && [ "$REG_PROJECT" != "$HARNESS_PROJECT_NAME" ]; then
|
|
102
|
+
echo "ERROR: slot registry projectName=${REG_PROJECT} does not match harness ${HARNESS_PROJECT_NAME}." >&2
|
|
103
|
+
exit 1
|
|
62
104
|
fi
|
|
63
105
|
if [ -z "$ENV_FILE" ]; then
|
|
64
|
-
echo "
|
|
106
|
+
echo "ERROR: .env.agent.${AGENT_ID} could not be resolved — relaunch with --replace when ready." >&2
|
|
107
|
+
exit 1
|
|
65
108
|
fi
|
|
66
109
|
elif [ -n "$ENV_FILE" ] && [ -f "$ENV_FILE" ]; then
|
|
67
110
|
echo "Agent ${AGENT_ID}: active (no PM2 processes)"
|
|
@@ -69,6 +112,8 @@ process.stdin.on('end', () => {
|
|
|
69
112
|
source "$ENV_FILE" 2>/dev/null || true
|
|
70
113
|
echo " Work dir: $(resolve_agent_work_dir "$ENV_FILE" "$AGENT_ID")"
|
|
71
114
|
[ -n "$WORKTREE_DIR" ] && [ -d "$WORKTREE_DIR" ] && echo " Worktree: $WORKTREE_DIR"
|
|
115
|
+
echo " Frontend: http://localhost:${FE_PORT}"
|
|
116
|
+
echo " API: http://localhost:${API_PORT}"
|
|
72
117
|
else
|
|
73
118
|
echo "No active environment for agent ${AGENT_ID}"
|
|
74
119
|
echo " Run: ./.har/launch.sh ${AGENT_ID}"
|
|
@@ -78,22 +123,23 @@ process.stdin.on('end', () => {
|
|
|
78
123
|
logs)
|
|
79
124
|
SERVICE="${3:-}"
|
|
80
125
|
if [ -n "$SERVICE" ]; then
|
|
81
|
-
npx pm2 logs "
|
|
126
|
+
npx pm2 logs "${PM2_SLOT_PREFIX}-${SERVICE}" --lines 100
|
|
82
127
|
else
|
|
83
|
-
npx pm2 logs --name "
|
|
128
|
+
npx pm2 logs --name "${PM2_SLOT_PREFIX}" --lines 100
|
|
84
129
|
fi
|
|
85
130
|
;;
|
|
86
131
|
|
|
87
132
|
restart)
|
|
88
133
|
SERVICE="${3:-}"
|
|
89
134
|
if [ -n "$SERVICE" ]; then
|
|
90
|
-
npx pm2 restart "
|
|
135
|
+
npx pm2 restart "${PM2_SLOT_PREFIX}-${SERVICE}"
|
|
91
136
|
else
|
|
92
137
|
npx pm2 jlist 2>/dev/null | node -e "
|
|
138
|
+
const prefix = '${PM2_SLOT_PREFIX}-';
|
|
93
139
|
const d = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
|
|
94
|
-
const names = d.filter(x => x.name.startsWith(
|
|
140
|
+
const names = d.filter(x => x.name && x.name.startsWith(prefix)).map(x => x.name);
|
|
95
141
|
names.forEach(n => require('child_process').execSync('npx pm2 restart ' + n, {stdio:'inherit'}));
|
|
96
|
-
if (names.length === 0) console.log('No processes found for
|
|
142
|
+
if (names.length === 0) console.log('No processes found for ${PM2_SLOT_PREFIX}');
|
|
97
143
|
" 2>/dev/null || true
|
|
98
144
|
fi
|
|
99
145
|
;;
|
|
@@ -122,9 +168,9 @@ try { console.log(JSON.stringify(JSON.parse(d), null, 2)); } catch { console.log
|
|
|
122
168
|
echo "Frontend: http://localhost:${FE_PORT}"
|
|
123
169
|
echo "API: http://localhost:${API_PORT}"
|
|
124
170
|
har_infra_enabled db && echo "Database: agent_${AGENT_ID} @ localhost:${DB_PORT}"
|
|
125
|
-
har_infra_enabled minio && echo "MinIO: http://localhost
|
|
126
|
-
har_infra_enabled headless-browser && echo "Browser: http://localhost
|
|
127
|
-
har_infra_enabled mailpit && echo "Mailpit: http://localhost
|
|
171
|
+
har_infra_enabled minio && echo "MinIO: http://localhost:${AGENT_MINIO_CONSOLE_PORT:-19001}"
|
|
172
|
+
har_infra_enabled headless-browser && echo "Browser: http://localhost:${AGENT_BROWSER_PORT:-13001}"
|
|
173
|
+
har_infra_enabled mailpit && echo "Mailpit: http://localhost:${AGENT_MAILPIT_WEB_PORT:-18025}"
|
|
128
174
|
;;
|
|
129
175
|
|
|
130
176
|
reset-db)
|
|
@@ -160,7 +206,7 @@ LIMIT 20;" 2>/dev/null || echo "pg_stat_statements extension not available"
|
|
|
160
206
|
;;
|
|
161
207
|
|
|
162
208
|
attach)
|
|
163
|
-
SESSION="
|
|
209
|
+
SESSION="$(har_tmux_session "$AGENT_ID")"
|
|
164
210
|
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
165
211
|
echo "No tmux session found: $SESSION" >&2
|
|
166
212
|
exit 1
|