amsd-pipeline-shared 0.5.23 → 0.5.25
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/bin/amsd-pipeline-shared.js +9 -2
- package/docs/gateway-baseline-2.0.80.md5 +1 -0
- package/docs/gateway-files.txt +2 -0
- package/docs/gateway-predecessors.md5 +3 -0
- package/orchestrations/scripts/lib/dashboard-target.sh +218 -0
- package/orchestrations/scripts/pre-run-reset.sh +1071 -0
- package/package.json +2 -1
|
@@ -20,6 +20,13 @@ const PKG = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'))
|
|
|
20
20
|
const FILES = fs.readFileSync(path.join(ROOT, 'docs', 'gateway-files.txt'), 'utf8').trim().split('\n').filter(Boolean);
|
|
21
21
|
const BASE = Object.fromEntries(fs.readFileSync(path.join(ROOT, 'docs', 'gateway-baseline-2.0.80.md5'), 'utf8').trim().split('\n')
|
|
22
22
|
.map((l) => l.trim().split(/\s+/)).map(([sum, f]) => [f, sum]));
|
|
23
|
+
// KNOWN PREDECESSOR COPIES (docs/gateway-predecessors.md5): a copy an install may hold that is neither the release nor this package's — a bash
|
|
24
|
+
// repo change made after 2.0.80 — named with its source; replaceable like the released copy, restored by revert
|
|
25
|
+
const PRED = {};
|
|
26
|
+
for (const l of fs.readFileSync(path.join(ROOT, 'docs', 'gateway-predecessors.md5'), 'utf8').split('\n')) {
|
|
27
|
+
const m = l.match(/^([0-9a-f]{32})\s+(\S+)/); if (m) (PRED[m[2]] = PRED[m[2]] || []).push(m[1]);
|
|
28
|
+
}
|
|
29
|
+
const known = (f, sum) => sum === BASE[f] || (PRED[f] || []).includes(sum);
|
|
23
30
|
const md5 = (f) => (fs.existsSync(f) ? crypto.createHash('md5').update(fs.readFileSync(f)).digest('hex') : null);
|
|
24
31
|
const fail = (m, code = 1) => { process.stderr.write(`amsd-pipeline-shared: ${m}\n`); process.exit(code); };
|
|
25
32
|
|
|
@@ -62,7 +69,7 @@ if (cmd === 'revert') {
|
|
|
62
69
|
if (fs.existsSync(b)) fs.copyFileSync(b, path.join(ORCH, f));
|
|
63
70
|
else if (!BASE[f]) fs.rmSync(path.join(ORCH, f), { force: true }); // this package's own file: the release has none
|
|
64
71
|
}
|
|
65
|
-
const back = FILES.filter((f) => (BASE[f] ? md5(path.join(ORCH, f))
|
|
72
|
+
const back = FILES.filter((f) => (BASE[f] ? !known(f, md5(path.join(ORCH, f))) : fs.existsSync(path.join(ORCH, f))));
|
|
66
73
|
if (back.length) fail(`revert incomplete — not the released copy:\n ${back.join('\n ')}`);
|
|
67
74
|
fs.rmSync(STATE, { recursive: true, force: true });
|
|
68
75
|
process.stdout.write(`amsd-pipeline-shared: reverted ${FILES.length} file(s) to the released copies\n`);
|
|
@@ -77,7 +84,7 @@ const plan = FILES.map((f) => {
|
|
|
77
84
|
if (now && now !== ours && PREV[f] === now) return { f, now, ours, state: 'previous' };
|
|
78
85
|
// a file the release never had (this package's own, e.g. lib/agent-messages.js): installed where absent
|
|
79
86
|
if (!BASE[f]) return { f, now, ours, state: now === ours ? 'current' : (now === null ? 'new' : 'other') };
|
|
80
|
-
return { f, now, ours, state: now === ours ? 'current' : (now
|
|
87
|
+
return { f, now, ours, state: now === ours ? 'current' : (known(f, now) ? 'released' : 'other') };
|
|
81
88
|
});
|
|
82
89
|
const other = plan.filter((p) => p.state === 'other');
|
|
83
90
|
if (other.length) {
|
|
@@ -244,3 +244,4 @@ d4992178558476af4c4c228e6aa63b2d scripts/lib/mint-and-spec.sh
|
|
|
244
244
|
fac1b26443bb52ba45ba072f4d654920 scripts/lib/story-guards.sh
|
|
245
245
|
ada237373615a26d924226184ed7614b scripts/lib/prd-change-review.sh
|
|
246
246
|
c2dfd2381db0242fdbc487a948fd7feb scripts/lib/agent-si.js
|
|
247
|
+
03bde875977ab741c54c3b38a236a496 scripts/pre-run-reset.sh
|
package/docs/gateway-files.txt
CHANGED
|
@@ -165,6 +165,7 @@ scripts/lib/content-retry.js
|
|
|
165
165
|
scripts/lib/control-plane.sh
|
|
166
166
|
scripts/lib/cost-emitter.js
|
|
167
167
|
scripts/lib/cost-record.sh
|
|
168
|
+
scripts/lib/dashboard-target.sh
|
|
168
169
|
scripts/lib/doctor/agent.js
|
|
169
170
|
scripts/lib/doctor/classify.js
|
|
170
171
|
scripts/lib/doctor/cli.js
|
|
@@ -254,6 +255,7 @@ scripts/lib/transcript-tool-calls.js
|
|
|
254
255
|
scripts/lib/worktree-rungs.sh
|
|
255
256
|
scripts/llm-handler.sh
|
|
256
257
|
scripts/mint-agents-step.js
|
|
258
|
+
scripts/pre-run-reset.sh
|
|
257
259
|
scripts/preflight-check.sh
|
|
258
260
|
scripts/preflight-prd-integrity.sh
|
|
259
261
|
scripts/preflight-static.sh
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Copies a pipeline install may hold that are neither the 2.0.80 release nor this package's, each with where it came from.
|
|
2
|
+
# The installer backs one up and replaces it as it does the released copy; revert puts it back. Any other copy is refused.
|
|
3
|
+
a4e6e7d7071dfc30dc4ae11eff9050db scripts/pre-run-reset.sh epam-cli a306218f7 (unreleased): ClickHouse removed from the agent-monitor recreate (operator: "CLICKHOUSE OUT")
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# THE DASHBOARDS SERVE A RUN — pre-run-reset.sh's Step 1, lifted into a function so every pipeline points them the same way (operator,
|
|
3
|
+
# 2026-10-22: launch checklist item 12, "Dashboards all up and running, controlled by eleventy", in scope for the Claude workflows).
|
|
4
|
+
# The lines below are pre-run-reset.sh's Step 1 as released (2.0.80), unchanged: the active-PRD and active-output-dir pointers, the compose
|
|
5
|
+
# override mounting the run's PRD directory at /prd-dir and its LOG_DIR at /logs-dir, nginx's alias, and agent-monitor recreated.
|
|
6
|
+
# point_dashboards_at_run <prd_file> <log_dir>
|
|
7
|
+
# What pre-run-reset.sh sets before its Step 1 is taken from the caller when set, else derived from this install as pre-run-reset.sh derives it.
|
|
8
|
+
# CLICKHOUSE OUT (operator; epam-cli a306218f7): the recreate passes no ClickHouse port — the one change from the released step.
|
|
9
|
+
point_dashboards_at_run() {
|
|
10
|
+
local PRD_FILE="${1:?prd file}" LOG_DIR="${2:?log dir}"
|
|
11
|
+
local _here; _here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
local REPO_ROOT="${REPO_ROOT:-$(cd "$_here/../../.." && pwd)}"
|
|
13
|
+
local PRD_PRESENT=1; [ -f "$PRD_FILE" ] || PRD_PRESENT=0
|
|
14
|
+
if [ -z "${OBS_PROJECT:-}" ]; then
|
|
15
|
+
local _identity_lib="$REPO_ROOT/orchestrations-installer/lib/isolated-compose-identity.sh"
|
|
16
|
+
[ -f "$_identity_lib" ] && . "$_identity_lib" || { echo "[dashboards] missing $_identity_lib — cannot resolve the observability project name" >&2; return 1; }
|
|
17
|
+
local OBS_PROJECT; OBS_PROJECT="$(isolated_project_name "$REPO_ROOT" obs)"
|
|
18
|
+
fi
|
|
19
|
+
local _STATE_FILE="${PIPELINE_SERVICES_STATE_FILE:-$REPO_ROOT/.pipeline-services-state.env}"
|
|
20
|
+
if [ -z "${OBS_DASHBOARD_PORT:-}" ] && [ -f "$_STATE_FILE" ]; then set -a; . "$_STATE_FILE"; set +a; fi
|
|
21
|
+
local COMPOSE_BASE="${COMPOSE_BASE:-$REPO_ROOT/docker-compose.observability.yml}"
|
|
22
|
+
local COMPOSE_OVERRIDE="${COMPOSE_OVERRIDE:-$REPO_ROOT/docker-compose.observability.override.yml}"
|
|
23
|
+
local DASHBOARD_STATE_DIR="${DASHBOARD_STATE_DIR:-$REPO_ROOT/orchestrations/dashboards}"
|
|
24
|
+
declare -F info >/dev/null 2>&1 || info() { echo "[dashboards] $*" >&2; }
|
|
25
|
+
declare -F success >/dev/null 2>&1 || success() { echo "[dashboards] ✓ $*" >&2; }
|
|
26
|
+
# ── Step 1: Wire agent-monitor to serve the live PRD ─────────────────────────
|
|
27
|
+
# Generate a compose override that mounts the PRD's PARENT DIRECTORY (not the
|
|
28
|
+
# file itself) at /prd-dir inside the nginx container, and patch nginx.conf's
|
|
29
|
+
# alias to the correct basename. nginx.conf routes GET /prd.json to that path.
|
|
30
|
+
#
|
|
31
|
+
# Directory mount, not file mount — deliberately. A single-FILE bind mount is
|
|
32
|
+
# fixed to the inode present at mount time; since the orchestration pipeline
|
|
33
|
+
# always writes the PRD via atomic tmp+rename (jq ... > tmp && mv tmp file /
|
|
34
|
+
# fs.renameSync — correct, corruption-safe, used throughout this codebase),
|
|
35
|
+
# every write creates a NEW inode at that path, silently orphaning a file
|
|
36
|
+
# mount the moment the PRD is rewritten after this script runs (found live
|
|
37
|
+
# 2026-07-13: the dashboard had been serving a frozen, stale PRD snapshot
|
|
38
|
+
# indefinitely). A directory mount doesn't have this problem — renames INSIDE
|
|
39
|
+
# the mounted directory are visible immediately, no container restart needed
|
|
40
|
+
# for subsequent PRD writes during the run.
|
|
41
|
+
# THE LOGS MOUNT DOES NOT DEPEND ON THE PRD. This whole block used to be skipped when the PRD did
|
|
42
|
+
# not exist yet, taking /logs-dir down with it — but LOG_DIR is this run's own log directory and is
|
|
43
|
+
# knowable with or without a PRD. The consequence was a chain: a fresh install has no prd.json (it
|
|
44
|
+
# is REGENERATED by ingest, and no longer committed — see .gitignore), so the dashboard mount was
|
|
45
|
+
# skipped, so nginx had no /logs-dir, so pre-flight's own reachability check failed
|
|
46
|
+
# ("nginx /logs/healing-events.jsonl not reachable") and refused the launch — on an install where
|
|
47
|
+
# nothing was actually wrong.
|
|
48
|
+
#
|
|
49
|
+
# /prd-dir is the only part that genuinely needs the file, so that is the only part now conditional.
|
|
50
|
+
# THE PRD'S DIRECTORY IS KNOWABLE EVEN WHEN THE FILE IS NOT THERE YET, and mounting it is the whole
|
|
51
|
+
# reason this is a DIRECTORY mount rather than a file mount: a file appearing inside a mounted
|
|
52
|
+
# directory is visible immediately, with no container restart.
|
|
53
|
+
#
|
|
54
|
+
# Conditioning the /prd-dir mount on the file already existing was half a fix. On a fresh install
|
|
55
|
+
# the PRD does not exist when this runs — ingest writes it minutes LATER — so the mount was skipped,
|
|
56
|
+
# nothing re-mounted afterwards, and nginx served 404 for /prd.json for the whole run. The dashboard
|
|
57
|
+
# showed "data offline" against a perfectly healthy run (live 2026-09-04, pipeline-tests-13).
|
|
58
|
+
PRD_DIR="$(dirname "$PRD_FILE")"
|
|
59
|
+
PRD_BASENAME="$(basename "$PRD_FILE")"
|
|
60
|
+
# The directory itself must exist to be mounted; docker would otherwise create it as root.
|
|
61
|
+
mkdir -p "$PRD_DIR" 2>/dev/null || true
|
|
62
|
+
|
|
63
|
+
if [ "$PRD_PRESENT" = "0" ]; then
|
|
64
|
+
info "Configuring agent-monitor to serve $PRD_BASENAME (written by ingest during this run) and this run's logs..."
|
|
65
|
+
else
|
|
66
|
+
info "Configuring agent-monitor to serve: $(basename "$PRD_FILE")..."
|
|
67
|
+
|
|
68
|
+
# Ensure the PRD file is world-readable so nginx (non-root) can serve it
|
|
69
|
+
chmod o+r "$PRD_FILE"
|
|
70
|
+
|
|
71
|
+
# build/snapshot.js (the Eleventy watcher — a separate Node process, no access
|
|
72
|
+
# to the Docker mounts above) needs its own way to know which PRD is active.
|
|
73
|
+
# It reads this pointer file directly; falls back to the old dead
|
|
74
|
+
# orchestrations/prd.json symlink when absent so an unrun/older setup doesn't
|
|
75
|
+
# break. Truncate-write (>), not atomic rename — this file itself must never
|
|
76
|
+
# be subject to the inode-staleness bug it exists to work around.
|
|
77
|
+
echo -n "$PRD_FILE" > "$DASHBOARD_STATE_DIR/.active-prd-path"
|
|
78
|
+
|
|
79
|
+
# Same pointer-file pattern for the project output directory: snapshot.js
|
|
80
|
+
# (the Eleventy watcher) reads EPAM_PROJECT_OUTPUT_DIR from its process env,
|
|
81
|
+
# but since pre-run-reset.sh is invoked as a subprocess (not sourced), the
|
|
82
|
+
# export above does not propagate to the parent shell or to the Eleventy
|
|
83
|
+
# watcher started later by run-agent-orchestration.sh. Writing the path here
|
|
84
|
+
# lets snapshot.js read it without requiring the env var in the process chain.
|
|
85
|
+
echo -n "$LOG_DIR" > "$DASHBOARD_STATE_DIR/.active-output-dir"
|
|
86
|
+
fi # end: the PRD-only half (pointer files, chmod, nginx alias below)
|
|
87
|
+
|
|
88
|
+
# /logs-dir gets the same directory-mount treatment as /prd-dir, and for the
|
|
89
|
+
# identical reason: step-status.json/agent-status.json/agent-activity.jsonl/
|
|
90
|
+
# phase-cost.jsonl are all rewritten via atomic tmp+rename or repeated append
|
|
91
|
+
# during a run, and for external-project runs LOG_DIR is a directory the
|
|
92
|
+
# dashboard has otherwise never been told about at all.
|
|
93
|
+
#
|
|
94
|
+
# WRITTEN WHETHER OR NOT A PRD EXISTS YET — the /prd-dir line is the only part that needs one.
|
|
95
|
+
{
|
|
96
|
+
echo "services:"
|
|
97
|
+
echo " agent-monitor:"
|
|
98
|
+
echo " volumes:"
|
|
99
|
+
echo " - ${PRD_DIR}:/prd-dir:ro"
|
|
100
|
+
echo " - ${LOG_DIR}:/logs-dir:ro"
|
|
101
|
+
} > "$COMPOSE_OVERRIDE"
|
|
102
|
+
|
|
103
|
+
# nginx.conf's /prd.json alias basename must match whatever PRD was passed —
|
|
104
|
+
# patch it in place (truncate-write via sed -i, not atomic rename, so this
|
|
105
|
+
# single-file mount for nginx.conf itself is not subject to the same bug).
|
|
106
|
+
# Pointed whether or not the file exists yet: the basename is known from --prd, and the alias must
|
|
107
|
+
# already be correct when ingest writes the file into the mounted directory mid-run.
|
|
108
|
+
sed -i "s|alias /prd-dir/[^;]*;|alias /prd-dir/${PRD_BASENAME};|" \
|
|
109
|
+
"$REPO_ROOT/orchestrations/dashboards/nginx.conf"
|
|
110
|
+
|
|
111
|
+
# --force-recreate, not just `up -d`: nginx.conf itself is STILL a single-
|
|
112
|
+
# FILE bind mount (the sed -i just above rewrites it via a new inode, same as
|
|
113
|
+
# every other atomic-write case this session). Plain `up -d` only recreates a
|
|
114
|
+
# container when the COMPOSE CONFIG changed — it does not notice an existing
|
|
115
|
+
# bind-mounted file's inode changing underneath it, so without
|
|
116
|
+
# --force-recreate the container keeps serving the nginx.conf it started
|
|
117
|
+
# with, silently ignoring this run's PRD-basename patch (found live
|
|
118
|
+
# 2026-07-13 verifying this exact fix — even the fix's own docs file wasn't
|
|
119
|
+
# safe from the bug it documents).
|
|
120
|
+
# THE COMPOSE THAT MATCHES THE INSTALL, resolved once.
|
|
121
|
+
#
|
|
122
|
+
# This shelled out to `docker compose` literally. A podman install therefore never remounted
|
|
123
|
+
# agent-monitor, so the run's own log dir was never served and the pre-flight gate
|
|
124
|
+
# "nginx /logs/healing-events.jsonl not reachable" failed on every podman run -- after the
|
|
125
|
+
# installer's health check had already reported the box "ready", because that check probes host
|
|
126
|
+
# ports and cannot see a mount that was never made.
|
|
127
|
+
#
|
|
128
|
+
# container_compose() already resolves the runtime and knows podman needs PODMAN_COMPOSE_PROVIDER
|
|
129
|
+
# and a real runtime dir, and the installer lib ships inside every install. Sourced, never
|
|
130
|
+
# reimplemented: a second copy of the resolution is how this drifted from install.sh in the first
|
|
131
|
+
# place.
|
|
132
|
+
_obs_runtime_lib() {
|
|
133
|
+
local _c
|
|
134
|
+
for _c in "${INSTALLER_LIB:-}" \
|
|
135
|
+
"$(dirname "${BASH_SOURCE[0]}")/../../orchestrations-installer/lib/container-runtime.sh" \
|
|
136
|
+
"${REPO_ROOT:-}/orchestrations-installer/lib/container-runtime.sh"; do
|
|
137
|
+
[ -n "$_c" ] && [ -f "$_c" ] && { printf '%s' "$_c"; return 0; }
|
|
138
|
+
done
|
|
139
|
+
return 1
|
|
140
|
+
}
|
|
141
|
+
_obs_runtime_name() {
|
|
142
|
+
local _l; _l="$(_obs_runtime_lib)" || { printf 'none'; return 1; }
|
|
143
|
+
# shellcheck disable=SC1090
|
|
144
|
+
. "$_l" 2>/dev/null || { printf 'none'; return 1; }
|
|
145
|
+
container_runtime 2>/dev/null || printf 'none'
|
|
146
|
+
}
|
|
147
|
+
_obs_have_runtime() {
|
|
148
|
+
local _n; _n="$(_obs_runtime_name)"
|
|
149
|
+
[ -n "$_n" ] && [ "$_n" != "none" ]
|
|
150
|
+
}
|
|
151
|
+
_obs_compose() {
|
|
152
|
+
local _l; _l="$(_obs_runtime_lib)" || return 1
|
|
153
|
+
# shellcheck disable=SC1090
|
|
154
|
+
. "$_l" 2>/dev/null || return 1
|
|
155
|
+
container_compose "$@"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# THE DASHBOARD RESTART IS THE ONLY SLOW STEP, AND A TEST DOES NOT NEED IT.
|
|
159
|
+
#
|
|
160
|
+
# `docker compose up --force-recreate` costs ~3 seconds. Everything else this script does — the
|
|
161
|
+
# roster restore, the PRD restore, clearing generated artefacts and prompts — is file work that
|
|
162
|
+
# takes milliseconds. A test that exercises the RESET therefore paid three seconds per case for a
|
|
163
|
+
# container it never looks at: two test files spent 23 of the 24 seconds my tests take, purely
|
|
164
|
+
# here, and that is the difference between a suite that gets run and one that does not.
|
|
165
|
+
#
|
|
166
|
+
# Skipped only when asked. A run never sets this, so the live path is unchanged.
|
|
167
|
+
# REPLACING THE CONTAINER, NOT RESTARTING IT.
|
|
168
|
+
#
|
|
169
|
+
# --force-recreate is honoured by docker compose and IGNORED by podman-compose, which restarts the
|
|
170
|
+
# existing container instead. A restarted container keeps the volumes it was created with, so the
|
|
171
|
+
# override this script just wrote -- carrying THIS run's /prd-dir and /logs-dir -- was reported as
|
|
172
|
+
# applied and had no effect. Live 2026-09-08, pipeline-tests-41: override written 08:28,
|
|
173
|
+
# agent-monitor created 08:23 at install time and merely started at 08:28, mounts holding neither
|
|
174
|
+
# /logs-dir nor /prd-dir, and the run then died at a pre-flight check for a /logs path nginx could
|
|
175
|
+
# not serve.
|
|
176
|
+
#
|
|
177
|
+
# The same class this file already records for nginx.conf ("without --force-recreate the container
|
|
178
|
+
# keeps serving the nginx.conf it started with") -- one runtime further down. Removing the service
|
|
179
|
+
# first makes the replacement explicit and true on both runtimes; --force-recreate stays for
|
|
180
|
+
# docker, where it is meaningful and cheap.
|
|
181
|
+
_obs_replace_agent_monitor() {
|
|
182
|
+
EPAM_OBS_SUBNET="${OBS_SUBNET:-}" \
|
|
183
|
+
EPAM_OBS_LANGFUSE_PORT="${OBS_LANGFUSE_PORT:-}" \
|
|
184
|
+
EPAM_OBS_DASHBOARD_PORT="${OBS_DASHBOARD_PORT:-}" \
|
|
185
|
+
EPAM_OBS_GRAFANA_PORT="${OBS_GRAFANA_PORT:-}" \
|
|
186
|
+
EPAM_PRD_DIR="$PRD_DIR" EPAM_LOG_DIR="$LOG_DIR" \
|
|
187
|
+
_obs_compose -f "$COMPOSE_BASE" -f "$COMPOSE_OVERRIDE" -p "$OBS_PROJECT" \
|
|
188
|
+
rm -sf agent-monitor >/dev/null 2>&1 || true
|
|
189
|
+
EPAM_OBS_SUBNET="${OBS_SUBNET:-}" \
|
|
190
|
+
EPAM_OBS_LANGFUSE_PORT="${OBS_LANGFUSE_PORT:-}" \
|
|
191
|
+
EPAM_OBS_DASHBOARD_PORT="${OBS_DASHBOARD_PORT:-}" \
|
|
192
|
+
EPAM_OBS_GRAFANA_PORT="${OBS_GRAFANA_PORT:-}" \
|
|
193
|
+
EPAM_PRD_DIR="$PRD_DIR" EPAM_LOG_DIR="$LOG_DIR" \
|
|
194
|
+
_obs_compose -f "$COMPOSE_BASE" -f "$COMPOSE_OVERRIDE" -p "$OBS_PROJECT" \
|
|
195
|
+
up -d --force-recreate agent-monitor 2>/dev/null
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if [ "${EPAM_SKIP_CONTAINER_RESTART:-0}" = "1" ]; then
|
|
199
|
+
info " Container restart skipped (EPAM_SKIP_CONTAINER_RESTART=1)"
|
|
200
|
+
|
|
201
|
+
elif _obs_replace_agent_monitor; then
|
|
202
|
+
if [ "$PRD_PRESENT" = "1" ]; then
|
|
203
|
+
success "agent-monitor restarted → /prd-dir = $PRD_DIR (serving $PRD_BASENAME), /logs-dir = $LOG_DIR"
|
|
204
|
+
else
|
|
205
|
+
success "agent-monitor restarted → /prd-dir = $PRD_DIR (serving $PRD_BASENAME once ingest writes it), /logs-dir = $LOG_DIR"
|
|
206
|
+
fi
|
|
207
|
+
elif ! _obs_have_runtime; then
|
|
208
|
+
info " no container runtime found — skipping container restart"
|
|
209
|
+
else
|
|
210
|
+
# NOT "docker is missing". The runtime IS here and the remount FAILED, which is a different
|
|
211
|
+
# fact and a different fix. The old text blamed an absent docker for every outcome, and on
|
|
212
|
+
# 2026-09-08 that sent the reader hunting containers "stuck in Created" while the real cause
|
|
213
|
+
# was this call naming a runtime the box does not use.
|
|
214
|
+
info " agent-monitor remount FAILED with $(_obs_runtime_name) — this run's /logs-dir and /prd-dir are NOT mounted;"
|
|
215
|
+
info " the pre-flight check 'nginx /logs/healing-events.jsonl not reachable' will fail because of this"
|
|
216
|
+
fi
|
|
217
|
+
return 0
|
|
218
|
+
}
|