@rubytech/create-realagent-code 0.1.614 → 0.1.615
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/package.json +1 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +5 -0
- package/payload/platform/plugins/browser/PLUGIN.md +1 -1
- package/payload/platform/scripts/__tests__/vnc-cdp-liveness.test.sh +270 -19
- package/payload/platform/scripts/vnc.sh +174 -71
- package/payload/server/server.js +422 -251
package/package.json
CHANGED
|
@@ -9,6 +9,11 @@ Invoked by the admin agent directly.
|
|
|
9
9
|
|
|
10
10
|
This is the platform's release timeline, newest first. Each entry shows the date it shipped and the version it shipped in, so you can tell the operator how current their install is. To compare, read the installed version from `capabilities-here` and match it against the versions below. Keep answers high level and in plain English; this is a summary, not a full commit log.
|
|
11
11
|
|
|
12
|
+
## 2026-08-17 (0.1.615)
|
|
13
|
+
|
|
14
|
+
- An install that shows the browser on a connected screen now runs the right browser, and brings it back when it stops. One setting is the authority for which mode is in use, and both modes are supervised.
|
|
15
|
+
- Headless browsers left behind by other tools are now counted every hour, with the profile that launched each one named. They accumulated on a machine with nothing identifying where they came from; a browser this platform supervises is excluded.
|
|
16
|
+
|
|
12
17
|
## 2026-08-16 (0.1.614)
|
|
13
18
|
|
|
14
19
|
- A routine that woke the assistant is no longer counted as having reached you. A wake is now recorded as a wake, and a run records the message that actually arrived in the chat, checked against what was really sent.
|
|
@@ -129,4 +129,4 @@ All tools are admin-allowlisted; none are exposed to public chat.
|
|
|
129
129
|
|
|
130
130
|
## Observability
|
|
131
131
|
|
|
132
|
-
A successful render emits `[browser-render] url=… rendered=true domBytes=…` on the server's stderr (captured by the mcp-spawn-tee log). Every other tool returns a structured outcome: `ok`, or one of `cdp-unreachable`, `session-lost`, `selector-not-found`, `option-not-found`, `wait-timeout`, `navigate-failed`, `load-timeout`, `evaluate-failed`, `command-timeout`, `pdf-failed`, `screenshot-failed`, `unsupported-key`. On a device with no running Chromium
|
|
132
|
+
A successful render emits `[browser-render] url=… rendered=true domBytes=…` on the server's stderr (captured by the mcp-spawn-tee log). Every other tool returns a structured outcome: `ok`, or one of `cdp-unreachable`, `session-lost`, `selector-not-found`, `option-not-found`, `wait-timeout`, `navigate-failed`, `load-timeout`, `evaluate-failed`, `command-timeout`, `pdf-failed`, `screenshot-failed`, `unsupported-key`. On a device with no running Chromium the tools return `cdp-unreachable` rather than hanging. In **both** display modes a Chromium that dies is self-healing: the brand's standing CDP-liveness check (run each `rss-sampler` cycle via `vnc.sh cdp-liveness`) relaunches it and logs `[vnc] op=cdp-liveness display-mode=<virtual|native> … reachable=<bool>` plus `[vnc] op=chromium-respawn display-mode=<mode> … outcome=<ready|failed>` to the service journal, so `cdp-unreachable` is transient (recovered within the check interval, bounded against hot-looping an unlaunchable browser) rather than dead-until-restart. The launch precondition differs: virtual needs the brand's Xtigervnc display up, native needs an active local graphical session, and a native install with no such session emits `outcome=declined reason=no-graphical-session` each cycle instead of launching. The check was previously a silent no-op on native installs, and this file claimed a `cdp-unreachable` there cleared on the next browser call; it did not — the on-demand launcher is reachable only from the admin browser-pane route and onboarding, both of which need an operator, so an agent's browser call found no browser and nothing started one.
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Bash harness for vnc.sh `cdp-liveness` — the standing Chromium respawn
|
|
3
|
-
# supervisor (Task 1191).
|
|
3
|
+
# supervisor (Task 1191), extended to both display modes by Task 2780.
|
|
4
4
|
#
|
|
5
|
-
# The supervisor reconciles expected-vs-actual: when the
|
|
6
|
-
# the
|
|
7
|
-
#
|
|
8
|
-
# restart, bounded so an unlaunchable Chromium never hot-loops.
|
|
5
|
+
# The supervisor reconciles expected-vs-actual: when the automation Chromium's
|
|
6
|
+
# CDP port is unreachable but the mode's launch precondition holds, it re-runs
|
|
7
|
+
# that mode's start path so a Chromium that died is restored WITHOUT a
|
|
8
|
+
# brand-service restart, bounded so an unlaunchable Chromium never hot-loops.
|
|
9
9
|
#
|
|
10
|
-
# Determinism without a real browser or real
|
|
11
|
-
# fakes onto the PATH used FOR THE vnc.sh SUBPROCESS ONLY (never
|
|
12
|
-
# own PATH):
|
|
10
|
+
# Determinism without a real browser, real ports or a real login session: the
|
|
11
|
+
# test injects fakes onto the PATH used FOR THE vnc.sh SUBPROCESS ONLY (never
|
|
12
|
+
# the harness's own PATH):
|
|
13
13
|
# ss — reports a port as LISTEN iff it appears in $STATE_DIR/ports.
|
|
14
14
|
# sleep — collapses vnc.sh's port-wait loop to ~0.02s/iteration.
|
|
15
15
|
# chromium-stub — records each launch in $STATE_DIR/chromium-calls and, in
|
|
16
16
|
# "ready" mode, writes the CDP port into $STATE_DIR/ports so
|
|
17
17
|
# the next ss probe sees it. "fail" mode never opens the port.
|
|
18
|
+
# loginctl — answers list-sessions / show-session from $STATE_DIR/sessions,
|
|
19
|
+
# one `id|active|remote|type|display` row per line, so session
|
|
20
|
+
# selection is exercised without a real seat (Task 2780).
|
|
21
|
+
# Xtigervnc — opens the rfb port so the `start` path can be driven.
|
|
22
|
+
# websockify — opens the websockify port for the same reason.
|
|
18
23
|
# RESPAWN_MAX_FAILS / RESPAWN_COOLDOWN_SECS are passed to vnc.sh so the backoff
|
|
19
24
|
# state machine is exercised without waiting real minutes.
|
|
20
25
|
#
|
|
@@ -24,7 +29,15 @@
|
|
|
24
29
|
# 3. repeated unreachable cycles → respawn capped at MAX, then cooldown
|
|
25
30
|
# (no hot-loop on an unlaunchable browser).
|
|
26
31
|
# 4. cooldown elapses → one further respawn attempt (not stuck).
|
|
27
|
-
#
|
|
32
|
+
# 6. virtual respawn is headed on the brand display.
|
|
33
|
+
# …and the Task 2780 surface:
|
|
34
|
+
# 5. native + CDP up → a line carrying display-mode=native.
|
|
35
|
+
# 7. native + CDP down + a session → one native respawn, on the session's own
|
|
36
|
+
# display.
|
|
37
|
+
# 8. native + no local graphical session → declines, launches nothing, and
|
|
38
|
+
# says so.
|
|
39
|
+
# 9. session selection is by evidence, not by row order.
|
|
40
|
+
# 10. `vnc.sh start` reads the display mode from one place, both arms.
|
|
28
41
|
|
|
29
42
|
set -uo pipefail
|
|
30
43
|
|
|
@@ -55,6 +68,8 @@ RFB_PORT=55900
|
|
|
55
68
|
WS_PORT=56080
|
|
56
69
|
VNC_NUM=99
|
|
57
70
|
|
|
71
|
+
RESPAWN_STATE="$MAXY_DIR/logs/cdp-respawn.state"
|
|
72
|
+
|
|
58
73
|
setup() {
|
|
59
74
|
rm -rf "$ROOT" "$MAXY_DIR"
|
|
60
75
|
mkdir -p "$ROOT/platform/scripts" "$ROOT/platform/config" "$STATE_DIR" "$FAKE_BIN" "$MAXY_DIR/logs"
|
|
@@ -74,11 +89,24 @@ JSON
|
|
|
74
89
|
# chromium-binary.path → the stub (a non-snap, executable path).
|
|
75
90
|
echo "$FAKE_BIN/chromium-stub" > "$ROOT/platform/config/chromium-binary.path"
|
|
76
91
|
|
|
92
|
+
# The `start` path resolves the websockify bind posture from this file and
|
|
93
|
+
# hard-exits when it is absent, so the start-path cases need it present.
|
|
94
|
+
echo "bind=127.0.0.1 kind=loopback" > "$ROOT/platform/config/websockify-bind"
|
|
95
|
+
|
|
77
96
|
: > "$STATE_DIR/ports"
|
|
78
97
|
echo "$CDP_PORT" > "$STATE_DIR/cdp-port"
|
|
79
98
|
echo "ready" > "$STATE_DIR/chromium-mode"
|
|
80
99
|
: > "$STATE_DIR/chromium-calls"
|
|
81
100
|
|
|
101
|
+
# One healthy local graphical session by default, so the virtual-mode cases
|
|
102
|
+
# are unaffected by session state and the native cases opt into their own.
|
|
103
|
+
printf '9|yes|no|x11|:1\n' > "$STATE_DIR/sessions"
|
|
104
|
+
|
|
105
|
+
# check_display_collision reads this lock BEFORE kill_stale clears it, so a
|
|
106
|
+
# stale file left by unrelated software on the dev machine would make the
|
|
107
|
+
# start-path cases exit 1 for a reason that is not the code under test.
|
|
108
|
+
rm -f "/tmp/.X${VNC_NUM}-lock"
|
|
109
|
+
|
|
82
110
|
# Fake ss: LISTEN line iff the requested port is in $STATE_DIR/ports.
|
|
83
111
|
cat > "$FAKE_BIN/ss" <<'SS'
|
|
84
112
|
#!/usr/bin/env bash
|
|
@@ -98,8 +126,9 @@ SLEEP
|
|
|
98
126
|
|
|
99
127
|
# Stub Chromium: record the launch — its argv and the DISPLAY it inherited,
|
|
100
128
|
# so a test can assert HOW Chromium was launched, not just that it was
|
|
101
|
-
# (Task 1562 turned the virtual-mode launch headless
|
|
102
|
-
# the
|
|
129
|
+
# (Task 1562 turned the virtual-mode launch headless; Task 2780 asserts the
|
|
130
|
+
# native launch lands on the discovered session's display). In "ready" mode
|
|
131
|
+
# open the CDP port.
|
|
103
132
|
cat > "$FAKE_BIN/chromium-stub" <<'STUB'
|
|
104
133
|
#!/usr/bin/env bash
|
|
105
134
|
echo "launch DISPLAY=${DISPLAY:-unset} $*" >> "$STATE_DIR/chromium-calls"
|
|
@@ -109,7 +138,60 @@ fi
|
|
|
109
138
|
exit 0
|
|
110
139
|
STUB
|
|
111
140
|
|
|
112
|
-
|
|
141
|
+
# Fake loginctl driven by $STATE_DIR/sessions (id|active|remote|type|display).
|
|
142
|
+
# list-sessions prints the systemd column layout so a row-order-dependent
|
|
143
|
+
# reader would still parse it — which is the point: Task 2780's regression is
|
|
144
|
+
# that taking row 1 picked a closing remote tty over the real graphical seat.
|
|
145
|
+
cat > "$FAKE_BIN/loginctl" <<'LOGINCTL'
|
|
146
|
+
#!/usr/bin/env bash
|
|
147
|
+
sessions_file="$STATE_DIR/sessions"
|
|
148
|
+
[ -f "$sessions_file" ] || exit 0
|
|
149
|
+
case "${1:-}" in
|
|
150
|
+
list-sessions)
|
|
151
|
+
while IFS='|' read -r id active remote stype sdisplay; do
|
|
152
|
+
[ -n "$id" ] || continue
|
|
153
|
+
echo "$id 1000 neo seat0 tty2"
|
|
154
|
+
done < "$sessions_file"
|
|
155
|
+
;;
|
|
156
|
+
show-session)
|
|
157
|
+
want_id="${2:-}"
|
|
158
|
+
prop=""
|
|
159
|
+
shift 2 2>/dev/null || true
|
|
160
|
+
while [ $# -gt 0 ]; do
|
|
161
|
+
if [ "$1" = "-p" ]; then prop="${2:-}"; shift; fi
|
|
162
|
+
shift
|
|
163
|
+
done
|
|
164
|
+
while IFS='|' read -r id active remote stype sdisplay; do
|
|
165
|
+
[ "$id" = "$want_id" ] || continue
|
|
166
|
+
case "$prop" in
|
|
167
|
+
Active) echo "$active" ;;
|
|
168
|
+
Remote) echo "$remote" ;;
|
|
169
|
+
Type) echo "$stype" ;;
|
|
170
|
+
Display) echo "$sdisplay" ;;
|
|
171
|
+
Leader) echo "" ;;
|
|
172
|
+
esac
|
|
173
|
+
done < "$sessions_file"
|
|
174
|
+
;;
|
|
175
|
+
esac
|
|
176
|
+
exit 0
|
|
177
|
+
LOGINCTL
|
|
178
|
+
|
|
179
|
+
# Display server and websocket bridge: open their ports so the `start` path
|
|
180
|
+
# gets past its two wait_for_port gates without a real X server.
|
|
181
|
+
cat > "$FAKE_BIN/Xtigervnc" <<XVNC
|
|
182
|
+
#!/usr/bin/env bash
|
|
183
|
+
echo "$RFB_PORT" >> "\$STATE_DIR/ports"
|
|
184
|
+
exit 0
|
|
185
|
+
XVNC
|
|
186
|
+
|
|
187
|
+
cat > "$FAKE_BIN/websockify" <<WSK
|
|
188
|
+
#!/usr/bin/env bash
|
|
189
|
+
echo "$WS_PORT" >> "\$STATE_DIR/ports"
|
|
190
|
+
exit 0
|
|
191
|
+
WSK
|
|
192
|
+
|
|
193
|
+
chmod +x "$FAKE_BIN/ss" "$FAKE_BIN/sleep" "$FAKE_BIN/chromium-stub" \
|
|
194
|
+
"$FAKE_BIN/loginctl" "$FAKE_BIN/Xtigervnc" "$FAKE_BIN/websockify"
|
|
113
195
|
}
|
|
114
196
|
|
|
115
197
|
teardown() {
|
|
@@ -126,6 +208,15 @@ run_liveness() {
|
|
|
126
208
|
bash "$ROOT/platform/scripts/vnc.sh" cdp-liveness 2>/dev/null
|
|
127
209
|
}
|
|
128
210
|
|
|
211
|
+
# Run the boot path. DISPLAY_MODE is deliberately absent from the environment:
|
|
212
|
+
# no unit file in the fleet exports it, which is the condition under which the
|
|
213
|
+
# split-read this task removes always resolved to "virtual".
|
|
214
|
+
run_start() {
|
|
215
|
+
STATE_DIR="$STATE_DIR" \
|
|
216
|
+
PATH="$FAKE_BIN:$PATH" \
|
|
217
|
+
bash "$ROOT/platform/scripts/vnc.sh" start >/dev/null 2>&1
|
|
218
|
+
}
|
|
219
|
+
|
|
129
220
|
write_env() {
|
|
130
221
|
printf 'DISPLAY_MODE=%s\n' "$1" > "$MAXY_DIR/.env"
|
|
131
222
|
}
|
|
@@ -133,6 +224,8 @@ write_env() {
|
|
|
133
224
|
chromium_calls() { local n; n=$(grep -c . "$STATE_DIR/chromium-calls" 2>/dev/null); echo "${n:-0}"; }
|
|
134
225
|
set_ports() { printf '%s\n' "$@" > "$STATE_DIR/ports"; }
|
|
135
226
|
set_mode() { echo "$1" > "$STATE_DIR/chromium-mode"; }
|
|
227
|
+
set_sessions() { printf '%s\n' "$@" > "$STATE_DIR/sessions"; }
|
|
228
|
+
boot_log() { cat "$MAXY_DIR/logs/vnc-boot.log" 2>/dev/null; }
|
|
136
229
|
|
|
137
230
|
assert() {
|
|
138
231
|
local desc="$1" cond="$2"
|
|
@@ -201,14 +294,20 @@ case_cooldown_retries() {
|
|
|
201
294
|
teardown
|
|
202
295
|
}
|
|
203
296
|
|
|
204
|
-
# --- Case 5: native
|
|
205
|
-
|
|
206
|
-
|
|
297
|
+
# --- Case 5: native + CDP up → a line, naming the mode -------------------
|
|
298
|
+
# Before Task 2780 this case asserted the opposite: native emitted NOTHING and
|
|
299
|
+
# supervised nothing. That silence is the defect — fourteen hours of no
|
|
300
|
+
# supervision and fourteen hours of healthy supervision produced identical
|
|
301
|
+
# journals — so the assertion is inverted rather than deleted.
|
|
302
|
+
case_native_reachable() {
|
|
303
|
+
echo "=== CASE 5: native + CDP up → reachable=true, mode named, no respawn ==="
|
|
207
304
|
setup; write_env native
|
|
208
|
-
set_ports "$RFB_PORT"
|
|
305
|
+
set_ports "$CDP_PORT" "$RFB_PORT"
|
|
209
306
|
out="$(run_liveness)"
|
|
210
|
-
assert "emits
|
|
211
|
-
assert "
|
|
307
|
+
assert "emits display-mode=native" "$(echo "$out" | grep -qE 'op=cdp-liveness .*display-mode=native' && echo 1 || echo 0)"
|
|
308
|
+
assert "emits reachable=true" "$(echo "$out" | grep -qE 'op=cdp-liveness .*reachable=true' && echo 1 || echo 0)"
|
|
309
|
+
assert "no chromium-respawn line" "$(echo "$out" | grep -q 'op=chromium-respawn' && echo 0 || echo 1)"
|
|
310
|
+
assert "zero chromium launches" "$([ "$(chromium_calls)" -eq 0 ] && echo 1 || echo 0)"
|
|
212
311
|
teardown
|
|
213
312
|
}
|
|
214
313
|
|
|
@@ -233,12 +332,164 @@ case_respawn_is_headed() {
|
|
|
233
332
|
teardown
|
|
234
333
|
}
|
|
235
334
|
|
|
335
|
+
# --- Case 7: native + CDP down + a graphical session → native respawn ----
|
|
336
|
+
case_native_respawns() {
|
|
337
|
+
echo "=== CASE 7: native + CDP down + graphical session → one native respawn ==="
|
|
338
|
+
setup; write_env native
|
|
339
|
+
set_sessions "9|yes|no|x11|:1"
|
|
340
|
+
set_ports "$RFB_PORT" # CDP down
|
|
341
|
+
set_mode ready
|
|
342
|
+
out="$(run_liveness)"
|
|
343
|
+
assert "emits reachable=false naming native" "$(echo "$out" | grep -qE 'op=cdp-liveness .*display-mode=native .*reachable=false' && echo 1 || echo 0)"
|
|
344
|
+
assert "emits chromium-respawn ready" "$(echo "$out" | grep -qE 'op=chromium-respawn .*display-mode=native .*outcome=ready' && echo 1 || echo 0)"
|
|
345
|
+
assert "exactly one chromium launch" "$([ "$(chromium_calls)" -eq 1 ] && echo 1 || echo 0)"
|
|
346
|
+
assert "launch lands on the session display" "$(grep -q "DISPLAY=:1 " "$STATE_DIR/chromium-calls" && echo 1 || echo 0)"
|
|
347
|
+
teardown
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
# --- Case 8: native + no local graphical session → decline, no launch ----
|
|
351
|
+
case_native_declines_without_session() {
|
|
352
|
+
echo "=== CASE 8: native + no active local graphical session → decline ==="
|
|
353
|
+
setup; write_env native
|
|
354
|
+
set_sessions "3|yes|yes|tty|" "4|no|no|x11|:1" # remote, then inactive
|
|
355
|
+
set_ports "$RFB_PORT"
|
|
356
|
+
set_mode ready
|
|
357
|
+
out="$(run_liveness)"
|
|
358
|
+
assert "still emits reachable=false" "$(echo "$out" | grep -qE 'op=cdp-liveness .*display-mode=native .*reachable=false' && echo 1 || echo 0)"
|
|
359
|
+
assert "emits outcome=declined" "$(echo "$out" | grep -qE 'op=chromium-respawn .*outcome=declined reason=no-graphical-session' && echo 1 || echo 0)"
|
|
360
|
+
assert "zero chromium launches" "$([ "$(chromium_calls)" -eq 0 ] && echo 1 || echo 0)"
|
|
361
|
+
assert "no respawn state written" "$([ ! -f "$RESPAWN_STATE" ] && echo 1 || echo 0)"
|
|
362
|
+
teardown
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
# --- Case 9: session chosen by evidence, not by row order ----------------
|
|
366
|
+
# On the laptop the first row is a remote tty in state closing and the real
|
|
367
|
+
# graphical session is last. Taking row 1 gave an empty Display, which the old
|
|
368
|
+
# code substituted with :0 — right screen by accident on that box, wrong or
|
|
369
|
+
# absent screen on any other.
|
|
370
|
+
#
|
|
371
|
+
# Row 7 is the derived case: it satisfies all three selection criteria but
|
|
372
|
+
# names no Display, so it carries no launch vector. Accepting it would put the
|
|
373
|
+
# guess back, just one row later.
|
|
374
|
+
case_native_session_selection() {
|
|
375
|
+
echo "=== CASE 9: graphical session is the last row → chosen over four decoys ==="
|
|
376
|
+
setup; write_env native
|
|
377
|
+
set_sessions "1|no|yes|tty|" "2|yes|yes|tty|" "5|yes|no|tty|" "7|yes|no|x11|" "9|yes|no|x11|:1"
|
|
378
|
+
set_ports "$RFB_PORT"
|
|
379
|
+
set_mode ready
|
|
380
|
+
run_liveness >/dev/null
|
|
381
|
+
local call; call="$(cat "$STATE_DIR/chromium-calls")"
|
|
382
|
+
assert "launch carries the graphical session display" "$(echo "$call" | grep -q "DISPLAY=:1 " && echo 1 || echo 0)"
|
|
383
|
+
assert "launch never falls back to :0" "$(echo "$call" | grep -q "DISPLAY=:0 " && echo 0 || echo 1)"
|
|
384
|
+
teardown
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
# --- Case 10: `start` reads the display mode from one place --------------
|
|
388
|
+
# The defect: resolve_display_mode() read the brand .env while the start case
|
|
389
|
+
# read the DISPLAY_MODE env var, which no unit file exports. Both arms run,
|
|
390
|
+
# because the bug was one arm being unreachable.
|
|
391
|
+
case_start_mode_single_authority() {
|
|
392
|
+
echo "=== CASE 10: vnc.sh start resolves the mode from one place, both arms ==="
|
|
393
|
+
setup; write_env native
|
|
394
|
+
set_sessions "9|yes|no|x11|:1"
|
|
395
|
+
run_start
|
|
396
|
+
assert "native arm logs mode=native" "$(boot_log | grep -q 'mode=native' && echo 1 || echo 0)"
|
|
397
|
+
assert "native arm boots no Chromium" "$([ "$(chromium_calls)" -eq 0 ] && echo 1 || echo 0)"
|
|
398
|
+
teardown
|
|
399
|
+
|
|
400
|
+
setup; write_env virtual
|
|
401
|
+
run_start
|
|
402
|
+
assert "virtual arm logs mode=virtual" "$(boot_log | grep -q 'mode=virtual' && echo 1 || echo 0)"
|
|
403
|
+
assert "virtual arm boots Chromium" "$([ "$(chromium_calls)" -ge 1 ] && echo 1 || echo 0)"
|
|
404
|
+
teardown
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
# --- Case 11: the on-demand path carries the same refusal ----------------
|
|
408
|
+
# `start-chrome-native` is what ensureCdp() shells for a native transport from
|
|
409
|
+
# the browser-pane route. It is a SEPARATE entry point from cdp-liveness and
|
|
410
|
+
# reaches start_chrome_native's own guard, which the liveness cases never do
|
|
411
|
+
# because cdp_liveness returns at its precondition first. ensureCdp runs this
|
|
412
|
+
# through execFileSync, which throws on a non-zero exit, so the exit status is
|
|
413
|
+
# the contract that turns a refusal into a reported failure rather than a
|
|
414
|
+
# silent success.
|
|
415
|
+
case_start_chrome_native_subcommand() {
|
|
416
|
+
echo "=== CASE 11: start-chrome-native refuses without a session, launches with one ==="
|
|
417
|
+
setup; write_env native
|
|
418
|
+
set_sessions "3|yes|yes|tty|" "4|no|no|x11|:1" # remote, then inactive
|
|
419
|
+
set_mode ready
|
|
420
|
+
out="$(STATE_DIR="$STATE_DIR" PATH="$FAKE_BIN:$PATH" bash "$ROOT/platform/scripts/vnc.sh" start-chrome-native 2>/dev/null)"
|
|
421
|
+
local rc=$?
|
|
422
|
+
assert "declines under op=chromium-start" "$(echo "$out" | grep -qE 'op=chromium-start .*outcome=declined reason=no-graphical-session' && echo 1 || echo 0)"
|
|
423
|
+
assert "exits non-zero so ensureCdp sees it" "$([ "$rc" -ne 0 ] && echo 1 || echo 0)"
|
|
424
|
+
assert "zero chromium launches" "$([ "$(chromium_calls)" -eq 0 ] && echo 1 || echo 0)"
|
|
425
|
+
teardown
|
|
426
|
+
|
|
427
|
+
setup; write_env native
|
|
428
|
+
set_sessions "9|yes|no|x11|:1"
|
|
429
|
+
set_mode ready
|
|
430
|
+
out="$(STATE_DIR="$STATE_DIR" PATH="$FAKE_BIN:$PATH" bash "$ROOT/platform/scripts/vnc.sh" start-chrome-native 2>/dev/null)"
|
|
431
|
+
assert "launches under op=chromium-start ready" "$(echo "$out" | grep -qE 'op=chromium-start display-mode=native mode=x11 .*outcome=ready' && echo 1 || echo 0)"
|
|
432
|
+
assert "launch lands on the session display" "$(grep -q "DISPLAY=:1 " "$STATE_DIR/chromium-calls" && echo 1 || echo 0)"
|
|
433
|
+
teardown
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
# --- Case 12: the cap and cooldown apply to native unchanged -------------
|
|
437
|
+
# Case 3 proves this for virtual. Native shares the state file, the cap and the
|
|
438
|
+
# cooldown, so a native box whose Chromium will not launch must back off the
|
|
439
|
+
# same way rather than relaunching every cycle.
|
|
440
|
+
case_native_backoff_no_hotloop() {
|
|
441
|
+
echo "=== CASE 12: native repeated launch failure → respawn capped at MAX ==="
|
|
442
|
+
setup; write_env native
|
|
443
|
+
set_sessions "9|yes|no|x11|:1" # session present, so it is not a decline
|
|
444
|
+
set_ports "$RFB_PORT" # CDP stays down
|
|
445
|
+
set_mode fail # every launch fails to open CDP
|
|
446
|
+
local i
|
|
447
|
+
for i in 1 2 3 4 5; do
|
|
448
|
+
RESPAWN_MAX_FAILS=3 RESPAWN_COOLDOWN_SECS=300 run_liveness >/dev/null
|
|
449
|
+
done
|
|
450
|
+
assert "native respawn capped at 3 over 5 cycles" "$([ "$(chromium_calls)" -eq 3 ] && echo 1 || echo 0)"
|
|
451
|
+
assert "native failure writes the respawn state" "$([ -f "$RESPAWN_STATE" ] && echo 1 || echo 0)"
|
|
452
|
+
teardown
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
# --- Case 13: the per-cycle path writes nothing to vnc-boot.log ----------
|
|
456
|
+
# vnc-boot.log is NOT one of logs-rotate.sh's targets, so nothing bounds it.
|
|
457
|
+
# vnc.sh already suppresses its one per-invocation line on the cdp-liveness
|
|
458
|
+
# path for exactly this reason. Native session discovery runs on that path
|
|
459
|
+
# every 30s, so its per-session detail must not land in that file either — a
|
|
460
|
+
# native box with no seat would otherwise grow it forever.
|
|
461
|
+
case_liveness_does_not_grow_boot_log() {
|
|
462
|
+
echo "=== CASE 13: cdp-liveness writes nothing to the unrotated vnc-boot.log ==="
|
|
463
|
+
setup; write_env native
|
|
464
|
+
set_sessions "3|yes|yes|tty|" "4|no|no|x11|:1" # no match → the chattiest path
|
|
465
|
+
set_ports "$RFB_PORT"
|
|
466
|
+
local i
|
|
467
|
+
for i in 1 2 3; do run_liveness >/dev/null; done
|
|
468
|
+
local lines; lines="$(boot_log | grep -c . 2>/dev/null)"; lines="${lines:-0}"
|
|
469
|
+
assert "three decline cycles add no vnc-boot.log lines" "$([ "$lines" -eq 0 ] && echo 1 || echo 0)"
|
|
470
|
+
teardown
|
|
471
|
+
|
|
472
|
+
# The same detail IS kept on the launch-attempt path, which is event-driven.
|
|
473
|
+
setup; write_env native
|
|
474
|
+
set_sessions "3|yes|yes|tty|" "4|no|no|x11|:1"
|
|
475
|
+
STATE_DIR="$STATE_DIR" PATH="$FAKE_BIN:$PATH" bash "$ROOT/platform/scripts/vnc.sh" start-chrome-native >/dev/null 2>&1
|
|
476
|
+
assert "start-chrome-native still records why it refused" "$(boot_log | grep -q 'no-graphical-session' && echo 1 || echo 0)"
|
|
477
|
+
teardown
|
|
478
|
+
}
|
|
479
|
+
|
|
236
480
|
case_reachable_noop
|
|
237
481
|
case_down_respawns_ready
|
|
238
482
|
case_backoff_no_hotloop
|
|
239
483
|
case_cooldown_retries
|
|
240
|
-
|
|
484
|
+
case_native_reachable
|
|
241
485
|
case_respawn_is_headed
|
|
486
|
+
case_native_respawns
|
|
487
|
+
case_native_declines_without_session
|
|
488
|
+
case_native_session_selection
|
|
489
|
+
case_start_mode_single_authority
|
|
490
|
+
case_start_chrome_native_subcommand
|
|
491
|
+
case_native_backoff_no_hotloop
|
|
492
|
+
case_liveness_does_not_grow_boot_log
|
|
242
493
|
|
|
243
494
|
echo ""
|
|
244
495
|
echo "==================================="
|