@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
|
@@ -26,6 +26,357 @@ validate_agent_id() {
|
|
|
26
26
|
fi
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
# ── Port allocation ─────────────────────────────────────────────────────────────
|
|
30
|
+
# Try the configured default first; scan the slot lane or infra range when busy.
|
|
31
|
+
|
|
32
|
+
port_in_use() {
|
|
33
|
+
local port="$1"
|
|
34
|
+
(exec 3<>"/dev/tcp/127.0.0.1/$port") 2>/dev/null && { exec 3>&- || true; return 0; }
|
|
35
|
+
return 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
har_port_step() {
|
|
39
|
+
echo "${HARNESS_PORT_STEP:-10}"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
har_default_app_port() {
|
|
43
|
+
local base="$1"
|
|
44
|
+
local agent_id="$2"
|
|
45
|
+
echo $(( base + agent_id * $(har_port_step) ))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
har_slot_port_lane_end() {
|
|
49
|
+
local default_port="$1"
|
|
50
|
+
echo $(( default_port + $(har_port_step) - 1 ))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# har_pick_free_port <start> <end> — echoes the first free port in [start, end].
|
|
54
|
+
har_pick_free_port() {
|
|
55
|
+
local start="$1"
|
|
56
|
+
local end="$2"
|
|
57
|
+
local port
|
|
58
|
+
for ((port=start; port<=end; port++)); do
|
|
59
|
+
if ! port_in_use "$port"; then
|
|
60
|
+
echo "$port"
|
|
61
|
+
return 0
|
|
62
|
+
fi
|
|
63
|
+
done
|
|
64
|
+
echo "Error: no free port in range ${start}-${end}" >&2
|
|
65
|
+
return 1
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# har_allocate_port <default> <scan_start> <scan_end>
|
|
69
|
+
har_allocate_port() {
|
|
70
|
+
local default_port="$1"
|
|
71
|
+
local scan_start="$2"
|
|
72
|
+
local scan_end="$3"
|
|
73
|
+
if ! port_in_use "$default_port"; then
|
|
74
|
+
echo "$default_port"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
har_pick_free_port "$scan_start" "$scan_end"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Allocate FE/API/DEBUG ports for a slot. Sets FE_PORT, API_PORT, DEBUG_PORT.
|
|
81
|
+
har_allocate_slot_app_ports() {
|
|
82
|
+
local agent_id="$1"
|
|
83
|
+
local step fe_default api_default debug_default
|
|
84
|
+
step="$(har_port_step)"
|
|
85
|
+
fe_default="$(har_default_app_port "${HARNESS_FE_BASE_PORT}" "$agent_id")"
|
|
86
|
+
api_default="$(har_default_app_port "${HARNESS_API_BASE_PORT}" "$agent_id")"
|
|
87
|
+
debug_default=$(( 9200 + agent_id * step ))
|
|
88
|
+
|
|
89
|
+
FE_PORT="$(har_allocate_port "$fe_default" "$fe_default" "$(har_slot_port_lane_end "$fe_default")")"
|
|
90
|
+
API_PORT="$(har_allocate_port "$api_default" "$api_default" "$(har_slot_port_lane_end "$api_default")")"
|
|
91
|
+
DEBUG_PORT="$(har_allocate_port "$debug_default" "$debug_default" $(( debug_default + step - 1 )) )"
|
|
92
|
+
export FE_PORT API_PORT DEBUG_PORT
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# ── Launch preflight (#36) ─────────────────────────────────────────────────────
|
|
96
|
+
# Readiness gate before worktree/install — ports, foreign PM2, Docker conflicts.
|
|
97
|
+
|
|
98
|
+
har_harness_uses_pm2() {
|
|
99
|
+
[ -f "${SCRIPT_DIR}/ecosystem.agent.template.cjs" ]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
har_port_docker_occupant() {
|
|
103
|
+
local port="$1"
|
|
104
|
+
docker ps --format '{{.Names}}\t{{.Ports}}' 2>/dev/null \
|
|
105
|
+
| grep -E ":${port}->|:${port}/" | head -1 | cut -f1 || true
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Detect port conflicts with `har control up` (Docker dashboard) — not project-specific;
|
|
109
|
+
# warns when the customer's packaged Mission Control container shares a slot app port.
|
|
110
|
+
har_check_control_port_conflict() {
|
|
111
|
+
local port="$1"
|
|
112
|
+
local name
|
|
113
|
+
name="$(docker ps --format '{{.Names}}\t{{.Ports}}' 2>/dev/null \
|
|
114
|
+
| grep -i control | grep -E ":${port}->|:${port}/" | head -1 | cut -f1 || true)"
|
|
115
|
+
if [ -n "$name" ]; then
|
|
116
|
+
echo "ERROR: har control up (container \"${name}\") occupies port ${port}." >&2
|
|
117
|
+
echo " Run: har control down — or use a different agent slot." >&2
|
|
118
|
+
return 1
|
|
119
|
+
fi
|
|
120
|
+
return 0
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
har_warn_control_on_default_port() {
|
|
124
|
+
local agent_id="$1"
|
|
125
|
+
har_harness_uses_pm2 || return 0
|
|
126
|
+
local default_port
|
|
127
|
+
default_port="$(har_default_app_port "${HARNESS_FE_BASE_PORT:-3000}" "$agent_id")"
|
|
128
|
+
if [ "$FE_PORT" = "$default_port" ]; then
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
local name
|
|
132
|
+
name="$(docker ps --format '{{.Names}}\t{{.Ports}}' 2>/dev/null \
|
|
133
|
+
| grep -i control | grep -E ":${default_port}->|:${default_port}/" | head -1 | cut -f1 || true)"
|
|
134
|
+
if [ -n "$name" ]; then
|
|
135
|
+
echo "WARN: har control up holds port ${default_port} (container \"${name}\")." >&2
|
|
136
|
+
echo " Harness will use port ${FE_PORT} instead. Run: har control down — to reclaim the default port." >&2
|
|
137
|
+
fi
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
har_check_foreign_pm2() {
|
|
141
|
+
local agent_id="$1"
|
|
142
|
+
local pm2_raw
|
|
143
|
+
har_harness_uses_pm2 || return 0
|
|
144
|
+
pm2_raw="$(npx --yes pm2 jlist 2>/dev/null || true)"
|
|
145
|
+
[ -n "$pm2_raw" ] || return 0
|
|
146
|
+
set +e
|
|
147
|
+
echo "$pm2_raw" | node -e "
|
|
148
|
+
const agentId = process.argv[1];
|
|
149
|
+
const project = process.argv[2];
|
|
150
|
+
const slotPrefix = 'har-' + project + '-agent-' + agentId + '-';
|
|
151
|
+
const legacyPrefix = 'agent-' + agentId + '-';
|
|
152
|
+
let raw = '';
|
|
153
|
+
process.stdin.on('data', c => raw += c);
|
|
154
|
+
process.stdin.on('end', () => {
|
|
155
|
+
try {
|
|
156
|
+
const arr = JSON.parse(raw);
|
|
157
|
+
if (!Array.isArray(arr)) process.exit(0);
|
|
158
|
+
const foreign = arr.filter(x =>
|
|
159
|
+
x.name && (
|
|
160
|
+
(x.name.startsWith('har-') && x.name.includes('-agent-' + agentId + '-') && !x.name.startsWith(slotPrefix)) ||
|
|
161
|
+
(x.name.startsWith(legacyPrefix) && !x.name.startsWith('har-'))
|
|
162
|
+
));
|
|
163
|
+
if (foreign.length === 0) process.exit(0);
|
|
164
|
+
console.error('ERROR: foreign PM2 processes match agent ' + agentId + ':');
|
|
165
|
+
foreign.forEach(p => {
|
|
166
|
+
const cwd = p.pm2_env?.pm_cwd || p.pm2_env?.cwd || 'unknown';
|
|
167
|
+
console.error(' ' + p.name + ' cwd=' + cwd);
|
|
168
|
+
});
|
|
169
|
+
console.error(' Stop the other harness session or use a different slot.');
|
|
170
|
+
process.exit(1);
|
|
171
|
+
} catch {
|
|
172
|
+
process.exit(0);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
" "$agent_id" "$HARNESS_PROJECT_NAME"
|
|
176
|
+
local rc=$?
|
|
177
|
+
set -e
|
|
178
|
+
return "$rc"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# ── Resume failed launch (#38) ───────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
slot_session_status() {
|
|
184
|
+
local agent_id="$1"
|
|
185
|
+
read_slot_field "$(slot_registry_file "$agent_id")" status || true
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# Exit 0 when the slot registry records a partial launch (failed or starting).
|
|
189
|
+
slot_is_resumable() {
|
|
190
|
+
local agent_id="$1"
|
|
191
|
+
local status
|
|
192
|
+
status="$(slot_session_status "$agent_id")"
|
|
193
|
+
[ "$status" = "failed" ] || [ "$status" = "starting" ]
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# Echo shell assignments for launch.sh — eval after validating resumability.
|
|
197
|
+
har_resume_session_assignments() {
|
|
198
|
+
local agent_id="$1"
|
|
199
|
+
local reg work_dir worktree branch suffix base_branch base_commit purpose mode env_file
|
|
200
|
+
|
|
201
|
+
if ! slot_is_resumable "$agent_id"; then
|
|
202
|
+
local status
|
|
203
|
+
status="$(slot_session_status "$agent_id")"
|
|
204
|
+
echo "echo \"ERROR: slot ${agent_id} is not resumable (status=${status:-none}; need failed or starting).\" >&2" >&2
|
|
205
|
+
echo "echo \" Use a normal launch, or --replace to start a fresh session.\" >&2"
|
|
206
|
+
echo 'exit 2'
|
|
207
|
+
return 1
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
reg="$(slot_registry_file "$agent_id")"
|
|
211
|
+
work_dir="$(read_slot_field "$reg" workDir || true)"
|
|
212
|
+
worktree="$(read_slot_field "$reg" worktreePath || true)"
|
|
213
|
+
branch="$(read_slot_field "$reg" branch || true)"
|
|
214
|
+
suffix="$(read_slot_field "$reg" suffix || true)"
|
|
215
|
+
base_branch="$(read_slot_field "$reg" baseBranch || true)"
|
|
216
|
+
base_commit="$(read_slot_field "$reg" baseCommit || true)"
|
|
217
|
+
purpose="$(read_slot_field "$reg" purpose || true)"
|
|
218
|
+
mode="$(read_slot_field "$reg" mode || true)"
|
|
219
|
+
|
|
220
|
+
if [ -z "$work_dir" ] || [ ! -d "$work_dir" ]; then
|
|
221
|
+
echo "echo \"ERROR: resume requires work dir from registry (missing or not found).\" >&2"
|
|
222
|
+
echo 'exit 1'
|
|
223
|
+
return 1
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
env_file="$work_dir/.env.agent.${agent_id}"
|
|
227
|
+
if [ ! -f "$env_file" ]; then
|
|
228
|
+
echo "echo \"ERROR: resume requires env file: ${env_file}\" >&2"
|
|
229
|
+
echo 'exit 1'
|
|
230
|
+
return 1
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
printf '%s\n' \
|
|
234
|
+
"WORK_DIR='${work_dir}'" \
|
|
235
|
+
"WORKTREE_DIR='${worktree}'" \
|
|
236
|
+
"BRANCH='${branch}'" \
|
|
237
|
+
"SUFFIX='${suffix}'" \
|
|
238
|
+
"BASE_BRANCH='${base_branch}'" \
|
|
239
|
+
"BASE_COMMIT='${base_commit}'" \
|
|
240
|
+
"PURPOSE='${purpose}'" \
|
|
241
|
+
"USE_WORKTREE=$([ "$mode" = worktree ] && echo true || echo false)" \
|
|
242
|
+
"ENV_FILE='${env_file}'"
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
# Skip provision-toolchain when deps were installed before the failed step.
|
|
246
|
+
har_toolchain_ready() {
|
|
247
|
+
local work_dir="$1"
|
|
248
|
+
if [ -f "$work_dir/package.json" ] && [ -d "$work_dir/node_modules" ]; then
|
|
249
|
+
return 0
|
|
250
|
+
fi
|
|
251
|
+
if [ -d "$work_dir/.har/venv" ]; then
|
|
252
|
+
return 0
|
|
253
|
+
fi
|
|
254
|
+
return 1
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# Rewrite agent env from env.template when ports changed during resume preflight.
|
|
258
|
+
har_regenerate_agent_env_file() {
|
|
259
|
+
local agent_id="$1"
|
|
260
|
+
local work_dir="$2"
|
|
261
|
+
local env_file="$3"
|
|
262
|
+
local template="${SCRIPT_DIR}/env.template"
|
|
263
|
+
if [ ! -f "$template" ]; then
|
|
264
|
+
return 0
|
|
265
|
+
fi
|
|
266
|
+
AGENT_ID="$agent_id" \
|
|
267
|
+
API_PORT="$API_PORT" \
|
|
268
|
+
FE_PORT="$FE_PORT" \
|
|
269
|
+
DEBUG_PORT="$DEBUG_PORT" \
|
|
270
|
+
DB_PORT="${DB_PORT:-${AGENT_DB_PORT:-${HARNESS_DB_PORT_DEFAULT:-15432}}}" \
|
|
271
|
+
MINIO_PORT="${MINIO_PORT:-${AGENT_MINIO_PORT:-${HARNESS_MINIO_PORT_DEFAULT:-19000}}}" \
|
|
272
|
+
BROWSER_PORT="${BROWSER_PORT:-${AGENT_BROWSER_PORT:-${HARNESS_BROWSER_PORT_DEFAULT:-13001}}}" \
|
|
273
|
+
REPO_ROOT="$work_dir" \
|
|
274
|
+
envsubst '${AGENT_ID} ${API_PORT} ${FE_PORT} ${DEBUG_PORT} ${DB_PORT} ${MINIO_PORT} ${BROWSER_PORT} ${REPO_ROOT}' \
|
|
275
|
+
< "$template" > "$env_file"
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
# har_launch_preflight <agent_id> <force> <replace> [resume]
|
|
279
|
+
# Exit 0 when ready; 2 when occupied/replace required; 1 on machine blockers.
|
|
280
|
+
# Sets FE_PORT/API_PORT/DEBUG_PORT when this harness uses PM2.
|
|
281
|
+
har_launch_preflight() {
|
|
282
|
+
local agent_id="$1"
|
|
283
|
+
local force="${2:-false}"
|
|
284
|
+
local replace="${3:-false}"
|
|
285
|
+
local resume="${4:-false}"
|
|
286
|
+
|
|
287
|
+
if [ "$resume" = true ]; then
|
|
288
|
+
if ! slot_is_resumable "$agent_id"; then
|
|
289
|
+
local status
|
|
290
|
+
status="$(slot_session_status "$agent_id")"
|
|
291
|
+
echo "ERROR: slot ${agent_id} is not resumable (status=${status:-none}; need failed or starting)." >&2
|
|
292
|
+
echo " Use a normal launch, or --replace to start a fresh session." >&2
|
|
293
|
+
return 2
|
|
294
|
+
fi
|
|
295
|
+
echo "==> [agent-${agent_id}] Resuming partial launch (worktree and deps preserved)..." >&2
|
|
296
|
+
elif slot_is_occupied "$agent_id"; then
|
|
297
|
+
if [ "$replace" != true ] && [ "${HAR_CONFIRM_REPLACE:-}" != "1" ]; then
|
|
298
|
+
print_slot_replace_warning "$agent_id"
|
|
299
|
+
echo "ERROR: slot ${agent_id} is occupied — pass --replace to proceed." >&2
|
|
300
|
+
return 2
|
|
301
|
+
fi
|
|
302
|
+
local wt
|
|
303
|
+
wt="$(existing_slot_worktree "$agent_id")"
|
|
304
|
+
if [ -n "$wt" ] && slot_worktree_dirty "$wt" && [ "$force" != true ]; then
|
|
305
|
+
echo "ERROR: dirty worktree requires --force after explicit user approval." >&2
|
|
306
|
+
return 2
|
|
307
|
+
fi
|
|
308
|
+
fi
|
|
309
|
+
|
|
310
|
+
if har_harness_uses_pm2; then
|
|
311
|
+
har_check_foreign_pm2 "$agent_id" || return 1
|
|
312
|
+
har_allocate_slot_app_ports "$agent_id" || return 1
|
|
313
|
+
har_warn_control_on_default_port "$agent_id"
|
|
314
|
+
local port occupant
|
|
315
|
+
for port in $(printf '%s\n' "$FE_PORT" "$API_PORT" | sort -u); do
|
|
316
|
+
har_check_control_port_conflict "$port" || return 1
|
|
317
|
+
occupant="$(har_port_docker_occupant "$port")"
|
|
318
|
+
if [ -n "$occupant" ] && [[ "$occupant" != har-${HARNESS_PROJECT_NAME}-* ]]; then
|
|
319
|
+
echo "ERROR: Docker container \"${occupant}\" binds port ${port}." >&2
|
|
320
|
+
echo " Stop it with: docker stop ${occupant}" >&2
|
|
321
|
+
return 1
|
|
322
|
+
fi
|
|
323
|
+
done
|
|
324
|
+
fi
|
|
325
|
+
return 0
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# Load persisted app/infra ports from .env.agent.<id> or the slot registry.
|
|
329
|
+
load_agent_ports() {
|
|
330
|
+
local agent_id="$1"
|
|
331
|
+
local repo_root="$2"
|
|
332
|
+
local env_file reg ports_json
|
|
333
|
+
env_file="$(resolve_agent_env_file "$agent_id" "$repo_root" 2>/dev/null || true)"
|
|
334
|
+
if [ -n "$env_file" ] && [ -f "$env_file" ]; then
|
|
335
|
+
# shellcheck source=/dev/null
|
|
336
|
+
source "$env_file"
|
|
337
|
+
FE_PORT="${FE_PORT:-}"
|
|
338
|
+
API_PORT="${API_PORT:-${PORT:-}}"
|
|
339
|
+
DEBUG_PORT="${DEBUG_PORT:-}"
|
|
340
|
+
DB_PORT="${PGPORT:-${DB_PORT:-}}"
|
|
341
|
+
return 0
|
|
342
|
+
fi
|
|
343
|
+
reg="$(slot_registry_file "$agent_id")"
|
|
344
|
+
if [ -f "$reg" ]; then
|
|
345
|
+
ports_json="$(node -e '
|
|
346
|
+
try {
|
|
347
|
+
const p = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")).ports;
|
|
348
|
+
if (p) process.stdout.write(JSON.stringify(p));
|
|
349
|
+
} catch {}
|
|
350
|
+
' "$reg" 2>/dev/null || true)"
|
|
351
|
+
if [ -n "$ports_json" ]; then
|
|
352
|
+
FE_PORT="$(node -e "const p=JSON.parse(process.argv[1]);process.stdout.write(String(p.frontend??''))" "$ports_json")"
|
|
353
|
+
API_PORT="$(node -e "const p=JSON.parse(process.argv[1]);process.stdout.write(String(p.api??''))" "$ports_json")"
|
|
354
|
+
DEBUG_PORT="$(node -e "const p=JSON.parse(process.argv[1]);process.stdout.write(String(p.debug??''))" "$ports_json")"
|
|
355
|
+
DB_PORT="$(node -e "const p=JSON.parse(process.argv[1]);process.stdout.write(String(p.db??''))" "$ports_json")"
|
|
356
|
+
export FE_PORT API_PORT DEBUG_PORT DB_PORT
|
|
357
|
+
return 0
|
|
358
|
+
fi
|
|
359
|
+
fi
|
|
360
|
+
har_allocate_slot_app_ports "$agent_id"
|
|
361
|
+
DB_PORT="${AGENT_DB_PORT:-${HARNESS_DB_PORT_DEFAULT:-15432}}"
|
|
362
|
+
export FE_PORT API_PORT DEBUG_PORT DB_PORT
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# ── Project-scoped PM2 names ────────────────────────────────────────────────────
|
|
366
|
+
# Pattern: har-<project>-agent-<id>-<service> (machine-global PM2 namespace).
|
|
367
|
+
|
|
368
|
+
har_pm2_slot_prefix() {
|
|
369
|
+
echo "har-${HARNESS_PROJECT_NAME}-agent-${1}"
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
har_pm2_delete_regex() {
|
|
373
|
+
echo "/^har-${HARNESS_PROJECT_NAME}-agent-${1}-/"
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
har_tmux_session() {
|
|
377
|
+
echo "har-${HARNESS_PROJECT_NAME}-agent-${1}"
|
|
378
|
+
}
|
|
379
|
+
|
|
29
380
|
# ── Slot registry ─────────────────────────────────────────────────────────────
|
|
30
381
|
# .har/slots/agent-<id>.json is the source of truth for where a session lives
|
|
31
382
|
# (worktree path, work dir, branch, base commit). Written by launch.sh, removed
|
|
@@ -4,8 +4,14 @@
|
|
|
4
4
|
# Usage: ./.har/attach.sh <agent-id>
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
# shellcheck source=/dev/null
|
|
9
|
+
source "$SCRIPT_DIR/harness.env"
|
|
10
|
+
# shellcheck source=/dev/null
|
|
11
|
+
source "$SCRIPT_DIR/agent-slot.sh"
|
|
12
|
+
|
|
7
13
|
AGENT_ID="${1:?Usage: attach.sh <agent-id>}"
|
|
8
|
-
SESSION="
|
|
14
|
+
SESSION="$(har_tmux_session "$AGENT_ID")"
|
|
9
15
|
|
|
10
16
|
if ! tmux has-session -t "$SESSION" 2>/dev/null; then
|
|
11
17
|
echo "No tmux session found: $SESSION" >&2
|
|
@@ -26,7 +26,7 @@ module.exports = {
|
|
|
26
26
|
// Example Node dev process. Replace with this repo's actual primary app
|
|
27
27
|
// command (Rails, Django, Go, Java, etc. should not keep this npm entry).
|
|
28
28
|
{
|
|
29
|
-
name: 'agent-${AGENT_ID}-api',
|
|
29
|
+
name: 'har-${HARNESS_PROJECT_NAME}-agent-${AGENT_ID}-api',
|
|
30
30
|
script: 'npm',
|
|
31
31
|
args: 'run dev',
|
|
32
32
|
interpreter: 'none',
|
|
@@ -11,10 +11,37 @@ export HARNESS_USE_WORKTREE=true
|
|
|
11
11
|
export HARNESS_PRIMARY_APP=app
|
|
12
12
|
|
|
13
13
|
export HARNESS_TEMPLATE_DB=template___PROJECT_NAME__
|
|
14
|
+
|
|
15
|
+
# ── Per-slot app ports (PM2 / primary app) ────────────────────────────────────
|
|
16
|
+
# Defaults: BASE + (AGENT_ID × HARNESS_PORT_STEP). launch.sh scans the slot lane
|
|
17
|
+
# when busy and writes resolved FE_PORT / API_PORT / DEBUG_PORT to .env.agent.<id>.
|
|
14
18
|
export HARNESS_FE_BASE_PORT=3000
|
|
15
19
|
export HARNESS_API_BASE_PORT=8000
|
|
20
|
+
export HARNESS_PORT_STEP=10
|
|
16
21
|
export HARNESS_HEALTH_CHECK_PATH=/health
|
|
17
22
|
|
|
23
|
+
# ── Shared infra host ports (one per machine) ─────────────────────────────────
|
|
24
|
+
# setup-infra.sh tries DEFAULT first, then scans SCAN_START..SCAN_END when busy.
|
|
25
|
+
# Persisted in .har/state/infra.env — do not hardcode these ports in app code.
|
|
26
|
+
export HARNESS_DB_PORT_DEFAULT=15432
|
|
27
|
+
export HARNESS_DB_PORT_SCAN_START=15432
|
|
28
|
+
export HARNESS_DB_PORT_SCAN_END=15499
|
|
29
|
+
export HARNESS_MINIO_PORT_DEFAULT=19000
|
|
30
|
+
export HARNESS_MINIO_PORT_SCAN_START=19000
|
|
31
|
+
export HARNESS_MINIO_PORT_SCAN_END=19099
|
|
32
|
+
export HARNESS_MINIO_CONSOLE_PORT_DEFAULT=19001
|
|
33
|
+
export HARNESS_MINIO_CONSOLE_PORT_SCAN_START=19001
|
|
34
|
+
export HARNESS_MINIO_CONSOLE_PORT_SCAN_END=19099
|
|
35
|
+
export HARNESS_BROWSER_PORT_DEFAULT=13001
|
|
36
|
+
export HARNESS_BROWSER_PORT_SCAN_START=13001
|
|
37
|
+
export HARNESS_BROWSER_PORT_SCAN_END=13099
|
|
38
|
+
export HARNESS_MAILPIT_WEB_PORT_DEFAULT=18025
|
|
39
|
+
export HARNESS_MAILPIT_WEB_PORT_SCAN_START=18025
|
|
40
|
+
export HARNESS_MAILPIT_WEB_PORT_SCAN_END=18099
|
|
41
|
+
export HARNESS_MAILPIT_SMTP_PORT_DEFAULT=11025
|
|
42
|
+
export HARNESS_MAILPIT_SMTP_PORT_SCAN_START=11025
|
|
43
|
+
export HARNESS_MAILPIT_SMTP_PORT_SCAN_END=11099
|
|
44
|
+
|
|
18
45
|
# Agent slot limits — set based on what your machine can run in parallel
|
|
19
46
|
export HARNESS_AGENT_SLOT_MIN=1
|
|
20
47
|
export HARNESS_AGENT_SLOT_MAX=5
|