@rubytech/create-maxy 1.0.801 → 1.0.803
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/cloudflare/PLUGIN.md +1 -2
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +7 -5
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +65 -186
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +2 -2
- package/payload/platform/plugins/whatsapp/PLUGIN.md +4 -2
- package/payload/server/chunk-3SCIVS2T.js +9910 -0
- package/payload/server/chunk-5ABJJQ5K.js +3465 -0
- package/payload/server/client-pool-S4UZCYDJ.js +29 -0
- package/payload/server/maxy-edge.js +2 -2
- package/payload/server/public/assets/{admin-Sa301b8q.js → admin-B41Gr-FL.js} +1 -1
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +376 -176
- package/payload/platform/plugins/cloudflare/scripts/_cdp-authorize-matcher.mjs +0 -74
- package/payload/platform/plugins/cloudflare/scripts/_cdp-authorize.mjs +0 -284
package/package.json
CHANGED
|
@@ -30,9 +30,8 @@ The plugin registers no agent-facing MCP tools (Task 554). Every Cloudflare oper
|
|
|
30
30
|
|
|
31
31
|
| Script | Purpose |
|
|
32
32
|
|---|---|
|
|
33
|
-
| [`scripts/setup-tunnel.sh`](scripts/setup-tunnel.sh) | Autonomous end-to-end setup: OAuth login, tunnel create, DNS route, config + state, service restart, post-restart verification. Invocation: `~/setup-tunnel.sh <brand> <port> <admin-hostname> [<public-hostname>] [<apex-hostname>]`. Apex hostnames print an `ACTION REQUIRED` block for the dashboard record the CLI cannot create. Task
|
|
33
|
+
| [`scripts/setup-tunnel.sh`](scripts/setup-tunnel.sh) | Autonomous end-to-end setup: OAuth login, tunnel create, DNS route, config + state, service restart, post-restart verification. Invocation: `~/setup-tunnel.sh <brand> <port> <admin-hostname> [<public-hostname>] [<apex-hostname>]`. Apex hostnames print an `ACTION REQUIRED` block for the dashboard record the CLI cannot create. Step 1 (Task 858 — wrappers faithfully relay third-party CLI) spawns `cloudflared tunnel login`, extracts the argotunnel URL from its stdout, mechanically opens it on the Pi VNC chromium (`DISPLAY=${DISPLAY:-:99} /usr/bin/chromium <url> &`), then polls for `~/.cloudflared/cert.pem` while the operator clicks the zone row + Authorize on the VNC. 180 s budget with a 2-second `step=oauth-login result=awaiting-cert` heartbeat. No CDP auto-click, no DOM matcher. |
|
|
34
34
|
| [`scripts/reset-tunnel.sh`](scripts/reset-tunnel.sh) | Deletes every tunnel on the brand's CF account and wipes `${CFG_DIR}`. Does not touch the platform service, stray CNAMEs, or token-mode connectors — those require dashboard cleanup or `pkill`. Invocation: `~/reset-tunnel.sh <brand>`. No polling blocks — every long-wait is bounded by `cloudflared`'s network round-trip, so no heartbeat contract applies. |
|
|
35
|
-
| [`scripts/_cdp-authorize.mjs`](scripts/_cdp-authorize.mjs) | Node 22 ESM helper driving the Cloudflare argotunnel Authorize click via CDP WebSocket. Self-contained (native `fetch` + `WebSocket`), invoked by `setup-tunnel.sh` with the target id returned from the `PUT /json/new?<url>` step. Exits 0 on successful click; 1 on `authorize-button-not-found` (the happy-path loud failure when the VNC browser isn't signed into Cloudflare); 2/3/4/5 on CDP/protocol errors. Emits one `cdp-authorize result=<…> reason=<…>` line to stdout per invocation, teed into the stream log under the `[setup-tunnel:cdp-click]` tag. |
|
|
36
35
|
|
|
37
36
|
### Skills
|
|
38
37
|
|
|
@@ -131,17 +131,19 @@ ls /tmp/.X11-unix/
|
|
|
131
131
|
|
|
132
132
|
You should see `X99`. If not, start VNC before retrying Step 1.
|
|
133
133
|
|
|
134
|
-
###
|
|
134
|
+
### States the consent page can render
|
|
135
135
|
|
|
136
|
-
The dashboard at `dash.cloudflare.com/argotunnel?...`
|
|
136
|
+
The dashboard at `dash.cloudflare.com/argotunnel?...` is `cloudflared`'s native consent surface. The automation wrapper does not inspect or auto-click it; the operator drives every variation manually on the VNC.
|
|
137
137
|
|
|
138
|
-
1. **Pre-authorize — the Authorize button is visible.**
|
|
138
|
+
1. **Pre-authorize — the Authorize button is visible.** Single-zone account. Click **Authorize**. Cloudflare's callback fires and `~/.cloudflared/cert.pem` lands a second or two later. Continue with the `mv` above.
|
|
139
139
|
|
|
140
|
-
2. **
|
|
140
|
+
2. **Multi-zone picker — "Please select the zone you want to add a Tunnel to" with a list of zones.** Multi-zone account. Click the zone row you want first; the page renders the Authorize button. Click **Authorize**. Same callback path as state 1.
|
|
141
|
+
|
|
142
|
+
3. **Post-success — "Cloudflared has installed a certificate" modal.** The bound account already has a cert authorised for the zones the dashboard knows about. The OAuth callback is idempotent — navigating to a fresh `cloudflared tunnel login` URL still fires the callback, so `~/.cloudflared/cert.pem` will land. Wait a second or two, then run the `mv`.
|
|
141
143
|
|
|
142
144
|
If the cert *also* exists at `${CFG_DIR}/cert.pem` from a prior partial run, Step 1 is already complete — skip to Step 2.
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
4. **Blank or button-less — neither the Authorize button nor the success modal is on the page.** The browser may be mid-load, signed out of Cloudflare, blocking on captcha, or showing some other state. Sign in to Cloudflare in the same browser, navigate back to the URL `cloudflared tunnel login` printed, and complete the click manually. If the URL has expired, kill `cloudflared` (`Ctrl+C`) and re-run Step 1 to get a fresh URL.
|
|
145
147
|
|
|
146
148
|
---
|
|
147
149
|
|
|
@@ -14,13 +14,16 @@
|
|
|
14
14
|
# hostnames. The script writes the ingress rule for them but prints an
|
|
15
15
|
# explicit ACTION REQUIRED message naming the manual dashboard step.
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
# URL
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
17
|
+
# Step 1 owns the browser-spawn deterministically. The wrapper extracts
|
|
18
|
+
# cloudflared's argotunnel URL from its stdout (regex below) and the moment
|
|
19
|
+
# the URL surfaces, mechanically opens it on the Pi's VNC chromium via
|
|
20
|
+
# `DISPLAY=${DISPLAY:-:99} /usr/bin/chromium <url> &` — no reliance on
|
|
21
|
+
# cloudflared's optimistic xdg-open, no CDP auto-click, no DOM matcher.
|
|
22
|
+
# The operator clicks the zone row + Authorize on the VNC themselves.
|
|
23
|
+
# This is the wrappers-faithfully-relay-third-party-cli doctrine (Task 858);
|
|
24
|
+
# any layer that auto-clicks Authorize for the operator is forbidden.
|
|
25
|
+
# cloudflared's stdout+stderr is teed line-by-line into STREAM_LOG_PATH so
|
|
26
|
+
# the chat UI's server-side tailer renders live progress in-turn.
|
|
24
27
|
|
|
25
28
|
set -euo pipefail
|
|
26
29
|
|
|
@@ -62,37 +65,43 @@ mkdir -p "${CFG_DIR}"
|
|
|
62
65
|
# --------------------------------------------------------------------------
|
|
63
66
|
# Step 1: OAuth login. Corresponds to runbook Step 1.
|
|
64
67
|
#
|
|
65
|
-
# Step 1 is a state machine over
|
|
66
|
-
# cert path
|
|
68
|
+
# Step 1 is a state machine over two observable variables — brand-scoped
|
|
69
|
+
# cert path and default cert path:
|
|
67
70
|
#
|
|
68
71
|
# ${CFG_DIR}/cert.pem present → Step 1 already done; skip.
|
|
69
72
|
# ${CFG_DIR}/cert.pem missing AND
|
|
70
73
|
# ~/.cloudflared/cert.pem present → pre-flight: a prior run
|
|
71
74
|
# reached the OAuth callback
|
|
72
75
|
# but failed before the mv;
|
|
73
|
-
# promote and skip
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
# poll for cert.pem,
|
|
76
|
+
# promote and skip cloudflared.
|
|
77
|
+
# both missing → spawn cloudflared, surface
|
|
78
|
+
# the URL on the Pi VNC,
|
|
79
|
+
# wait for the operator's
|
|
80
|
+
# click, poll for cert.pem,
|
|
81
|
+
# mv.
|
|
78
82
|
#
|
|
79
|
-
# Control flow when both certs are missing (Task
|
|
80
|
-
#
|
|
81
|
-
#
|
|
83
|
+
# Control flow when both certs are missing (Task 858 — wrappers faithfully
|
|
84
|
+
# relay third-party CLI):
|
|
85
|
+
# 1. Spawn cloudflared with stdout+stderr teed line-by-line to
|
|
82
86
|
# $STREAM_LOG_PATH with prefix [script:setup-tunnel:cloudflared]
|
|
83
87
|
# (Task 605's chat-surface namespace — see _stream-log.sh header).
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
88
|
+
# 2. Extract the authorize URL with a tolerant regex as it streams.
|
|
89
|
+
# 3. The instant the URL is extracted, mechanically open it on the Pi
|
|
90
|
+
# VNC chromium via `DISPLAY=${DISPLAY:-:99} /usr/bin/chromium <url> &`.
|
|
91
|
+
# Fire-and-forget — chromium is already running with CDP enabled at
|
|
92
|
+
# :9222, so the invocation IPCs the URL into the running instance as
|
|
93
|
+
# a new tab. The spawn is intentionally NOT tracked in the EXIT trap:
|
|
94
|
+
# it is a sibling open, not a child of cloudflared, and an orphaned
|
|
95
|
+
# late-arriving tab is harmless. Replaces cloudflared's own optimistic
|
|
96
|
+
# xdg-open which does not reliably target VNC :99 in this environment.
|
|
97
|
+
# 4. Emit `step=browser-spawn` and `step=browser-drive mode=operator-click`
|
|
98
|
+
# so the stream log records the surface state.
|
|
99
|
+
# 5. Wait for ~/.cloudflared/cert.pem to land (180 s budget — operator
|
|
100
|
+
# must click the zone row + Authorize on VNC).
|
|
101
|
+
# 6. Move cert.pem into the brand-scoped path.
|
|
93
102
|
#
|
|
94
|
-
#
|
|
95
|
-
#
|
|
103
|
+
# No CDP auto-click. No DOM matcher. No consent-page driver of any kind.
|
|
104
|
+
# Every failure branch exits 1 loudly naming the cause.
|
|
96
105
|
# --------------------------------------------------------------------------
|
|
97
106
|
|
|
98
107
|
# Pre-flight cert-promotion (Task 855). When ${CFG_DIR}/cert.pem is missing
|
|
@@ -131,20 +140,6 @@ fi
|
|
|
131
140
|
if [ ! -f "${CFG_DIR}/cert.pem" ]; then
|
|
132
141
|
phase_line setup-tunnel step=oauth-login cert_path="${CFG_DIR}/cert.pem" display="${DISPLAY:-:99}"
|
|
133
142
|
|
|
134
|
-
# CDP precheck — if Chromium DevTools is answering, we'll drive the
|
|
135
|
-
# Authorize click for the operator. If it isn't (action runner on a
|
|
136
|
-
# device without VNC — Task 664), we fall back to the ActionLogPanel
|
|
137
|
-
# URL-to-button flow: extract the URL, emit it as `OAUTH_URL: <url>`,
|
|
138
|
-
# and wait for cert.pem to land from the operator's own browser click.
|
|
139
|
-
CDP_AVAILABLE=1
|
|
140
|
-
if ! curl -sf --max-time 2 "http://127.0.0.1:9222/json/version" > /dev/null 2>&1; then
|
|
141
|
-
phase_line setup-tunnel step=oauth-login cdp=unavailable \
|
|
142
|
-
endpoint=http://127.0.0.1:9222 mode=operator-browser-fallback
|
|
143
|
-
CDP_AVAILABLE=0
|
|
144
|
-
else
|
|
145
|
-
phase_line setup-tunnel step=oauth-login cdp=ok
|
|
146
|
-
fi
|
|
147
|
-
|
|
148
143
|
URL_FILE="$(mktemp -t maxy-setup-tunnel-url.XXXXXX)"
|
|
149
144
|
LAST_LINE_FILE="$(mktemp -t maxy-setup-tunnel-last.XXXXXX)"
|
|
150
145
|
: > "${URL_FILE}"
|
|
@@ -210,154 +205,38 @@ if [ ! -f "${CFG_DIR}/cert.pem" ]; then
|
|
|
210
205
|
phase_line setup-tunnel step=browser-drive url_extracted=1
|
|
211
206
|
|
|
212
207
|
# Emit the URL on stdout in a shape ActionLogPanel's regex captures.
|
|
213
|
-
#
|
|
214
|
-
#
|
|
215
|
-
#
|
|
216
|
-
#
|
|
217
|
-
# available even when CDP succeeds — same-URL clicks are idempotent.
|
|
208
|
+
# The button is a redundant re-spawn fallback — by the time the chat-side
|
|
209
|
+
# ActionLogPanel renders, the page is already on the Pi VNC chromium per
|
|
210
|
+
# the spawn below. Button click POSTs to /api/admin/device-browser/navigate
|
|
211
|
+
# to re-target the same URL on the VNC; same end-effect as this spawn.
|
|
218
212
|
printf 'OAUTH_URL: %s\n' "${AUTH_URL}"
|
|
219
213
|
|
|
220
|
-
#
|
|
221
|
-
#
|
|
222
|
-
#
|
|
223
|
-
#
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
#
|
|
231
|
-
#
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
239
|
-
phase_line setup-tunnel step=browser-drive result=error reason=cdp-put-failed
|
|
240
|
-
echo "ERROR: CDP PUT /json/new?<url> returned empty — Chromium rejected the navigate." >&2
|
|
241
|
-
exit 1
|
|
242
|
-
fi
|
|
243
|
-
# Extract the CDP target id so _cdp-authorize.mjs can open a debugger
|
|
244
|
-
# WebSocket against the correct tab. The PUT response is a JSON object
|
|
245
|
-
# describing the newly-created target; `.id` is the only stable field
|
|
246
|
-
# across Chromium versions that works for `/json/list` lookup.
|
|
247
|
-
CDP_TARGET_ID="$(printf '%s' "${CDP_RESP}" | jq -r '.id // empty' 2>/dev/null || true)"
|
|
248
|
-
if [ -z "${CDP_TARGET_ID}" ]; then
|
|
249
|
-
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
250
|
-
phase_line setup-tunnel step=browser-drive result=error reason=cdp-target-id-missing \
|
|
251
|
-
resp_head="$(printf '%s' "${CDP_RESP}" | head -c 200)"
|
|
252
|
-
echo "ERROR: CDP PUT /json/new?<url> returned a body without an .id field — Chromium protocol drift?" >&2
|
|
253
|
-
exit 1
|
|
254
|
-
fi
|
|
255
|
-
phase_line setup-tunnel step=browser-drive result=accepted target_id="${CDP_TARGET_ID}"
|
|
256
|
-
|
|
257
|
-
# Task 588: drive the Authorize click via CDP WebSocket instead of waiting
|
|
258
|
-
# for a human. The helper shares a directory with this script; same
|
|
259
|
-
# readlink-resolve pattern as _stream-log.sh so the symlink install path
|
|
260
|
-
# (packages/create-maxy installs ~/setup-tunnel.sh as a symlink) doesn't
|
|
261
|
-
# point dirname at $HOME.
|
|
262
|
-
AUTH_HELPER="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/_cdp-authorize.mjs"
|
|
263
|
-
if [ ! -x "${AUTH_HELPER}" ]; then
|
|
264
|
-
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
265
|
-
phase_line setup-tunnel step=browser-drive result=error reason=cdp-helper-missing \
|
|
266
|
-
path="${AUTH_HELPER}"
|
|
267
|
-
echo "ERROR: _cdp-authorize.mjs helper missing or not executable at ${AUTH_HELPER}" >&2
|
|
268
|
-
exit 1
|
|
269
|
-
fi
|
|
270
|
-
# tee_subprocess_capture pipes the helper's `cdp-authorize result=…` line
|
|
271
|
-
# into the stream log under the setup-tunnel:cdp-click tag (live-tailable)
|
|
272
|
-
# AND through this shell's stdout so the wrapper can capture it for the
|
|
273
|
-
# tri-state reason discriminator (Task 855). The helper writes exactly
|
|
274
|
-
# one such line per invocation and exits; the shape is its contract:
|
|
275
|
-
# `cdp-authorize result=<ok|error> reason=<token> [k=v …]`.
|
|
276
|
-
# The wrapper's awk takes the LAST `cdp-authorize result=ok ` line so that
|
|
277
|
-
# any future emit() additions cannot mis-route the discriminator — the
|
|
278
|
-
# success line is always the last one before exit.
|
|
279
|
-
HELPER_OUT="$(mktemp -t maxy-cdp-authorize-out.XXXXXX)"
|
|
280
|
-
if tee_subprocess_capture setup-tunnel:cdp-click -- \
|
|
281
|
-
node "${AUTH_HELPER}" "${CDP_TARGET_ID}" \
|
|
282
|
-
> "${HELPER_OUT}"; then
|
|
283
|
-
HELPER_REASON="$(awk '/^cdp-authorize result=ok / {m=$0} END {if (m) {match(m, /reason=[a-z0-9-]+/); print substr(m, RSTART+7, RLENGTH-7)}}' "${HELPER_OUT}")"
|
|
284
|
-
rm -f "${HELPER_OUT}"
|
|
285
|
-
case "${HELPER_REASON}" in
|
|
286
|
-
clicked)
|
|
287
|
-
phase_line setup-tunnel step=browser-drive result=ok \
|
|
288
|
-
reason=clicked target_id="${CDP_TARGET_ID}"
|
|
289
|
-
;;
|
|
290
|
-
cert-already-installed)
|
|
291
|
-
# The dashboard rendered the Success modal — the bound account
|
|
292
|
-
# already has a cert. The OAuth callback URL was exercised by this
|
|
293
|
-
# navigation, so the in-flight cloudflared subprocess receives the
|
|
294
|
-
# idempotent callback and writes ~/.cloudflared/cert.pem; the poll
|
|
295
|
-
# below picks it up.
|
|
296
|
-
phase_line setup-tunnel step=browser-drive result=ok \
|
|
297
|
-
reason=cert-already-installed target_id="${CDP_TARGET_ID}"
|
|
298
|
-
;;
|
|
299
|
-
*)
|
|
300
|
-
# Helper exited 0 but emitted a reason this wrapper does not
|
|
301
|
-
# recognise — protocol drift between helper and wrapper. Loud-fail
|
|
302
|
-
# rather than silently fall through to the cert-poll, which would
|
|
303
|
-
# mask the cause if the cert never lands.
|
|
304
|
-
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
305
|
-
phase_line setup-tunnel step=browser-drive result=error \
|
|
306
|
-
reason=helper-unknown-success-reason raw="${HELPER_REASON:-empty}"
|
|
307
|
-
echo "ERROR: CDP helper exited 0 with unrecognised reason: ${HELPER_REASON:-empty}" >&2
|
|
308
|
-
echo " Expected reason=clicked or reason=cert-already-installed." >&2
|
|
309
|
-
echo " Helper / wrapper protocol drift — investigate stream log." >&2
|
|
310
|
-
exit 1
|
|
311
|
-
;;
|
|
312
|
-
esac
|
|
313
|
-
else
|
|
314
|
-
CLICK_RC=$?
|
|
315
|
-
rm -f "${HELPER_OUT}"
|
|
316
|
-
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
317
|
-
# Helper exit codes (mapped 1:1 to reason= so stream-log grep is precise;
|
|
318
|
-
# a catch-all reason would defeat the observability contract):
|
|
319
|
-
# 1 = authorize-button-not-found (loud — neither button nor success
|
|
320
|
-
# modal matched before BUTTON_POLL_TIMEOUT_MS; the form surfaces
|
|
321
|
-
# a verbatim quote of references/dashboard-guide.md "Authorise a
|
|
322
|
-
# new tunnel" so the operator can recover without SSH).
|
|
323
|
-
# 2 = cdp-ws-unreachable, 3 = target-not-found, 4 = click-evaluate-threw,
|
|
324
|
-
# 5 = protocol-error. All are final — no retry, no silent fallback.
|
|
325
|
-
case "${CLICK_RC}" in
|
|
326
|
-
1) CLICK_REASON=authorize-button-not-found ;;
|
|
327
|
-
2) CLICK_REASON=cdp-ws-unreachable ;;
|
|
328
|
-
3) CLICK_REASON=target-not-found ;;
|
|
329
|
-
4) CLICK_REASON=click-evaluate-threw ;;
|
|
330
|
-
5) CLICK_REASON=protocol-error ;;
|
|
331
|
-
*) CLICK_REASON="unknown-exit-${CLICK_RC}" ;;
|
|
332
|
-
esac
|
|
333
|
-
phase_line setup-tunnel step=browser-drive result=error \
|
|
334
|
-
reason="${CLICK_REASON}" click_rc="${CLICK_RC}"
|
|
335
|
-
echo "ERROR: CDP-driven Authorize click failed (helper exit=${CLICK_RC}, reason=${CLICK_REASON})." >&2
|
|
336
|
-
if [ "${CLICK_REASON}" = "authorize-button-not-found" ]; then
|
|
337
|
-
echo " The dashboard did not present an Authorize button or a Success modal." >&2
|
|
338
|
-
echo " The form will surface the dashboard click-path for manual recovery." >&2
|
|
339
|
-
else
|
|
340
|
-
echo " The CDP helper failed before reaching a terminal page state." >&2
|
|
341
|
-
echo " Re-run setup — or run ~/reset-tunnel.sh first if state is corrupt." >&2
|
|
342
|
-
fi
|
|
343
|
-
exit 1
|
|
344
|
-
fi
|
|
345
|
-
fi # end CDP-available branch (Task 664)
|
|
214
|
+
# Mechanically open the URL on the Pi VNC chromium (Task 858). Chromium
|
|
215
|
+
# is already running on :99 with CDP enabled (vnc.sh start_chrome at boot);
|
|
216
|
+
# invoking `/usr/bin/chromium <url>` against a running instance IPCs the
|
|
217
|
+
# URL into it as a new tab. Fire-and-forget — the spawn is intentionally
|
|
218
|
+
# NOT tracked in cleanup_oauth's EXIT trap because it is a sibling open,
|
|
219
|
+
# not a child of cloudflared, and an orphaned late-arriving tab is harmless.
|
|
220
|
+
# Replaces cloudflared's own optimistic xdg-open, which does not reliably
|
|
221
|
+
# target VNC :99 in this environment.
|
|
222
|
+
#
|
|
223
|
+
# Binary path: /usr/bin/chromium is the canonical runtime binary on every
|
|
224
|
+
# supported distro (Bookworm + Noble — see vnc.sh and packages/create-maxy/
|
|
225
|
+
# src/apt-resolve.ts). The Ubuntu transitional `chromium-browser` does not
|
|
226
|
+
# exist on Bookworm Pis, so using the absolute path is the only spelling
|
|
227
|
+
# that survives both distros.
|
|
228
|
+
DISPLAY="${DISPLAY:-:99}" /usr/bin/chromium "${AUTH_URL}" >/dev/null 2>&1 &
|
|
229
|
+
phase_line setup-tunnel step=browser-spawn result=ok \
|
|
230
|
+
display="${DISPLAY:-:99}" url_extracted=1
|
|
231
|
+
phase_line setup-tunnel step=browser-drive mode=operator-click url="${AUTH_URL}"
|
|
346
232
|
|
|
347
233
|
# Wait for cert.pem to land — cloudflared writes to ~/.cloudflared/cert.pem
|
|
348
|
-
# regardless of --origincert, so watch the canonical location.
|
|
349
|
-
#
|
|
350
|
-
#
|
|
351
|
-
# 2-second heartbeat inside the loop is the observability contract
|
|
352
|
-
#
|
|
353
|
-
|
|
354
|
-
# Task 664: operator-click path (no CDP) needs a human-paced window;
|
|
355
|
-
# CDP auto-click stays on the 20s round-trip budget.
|
|
356
|
-
if [ "${CDP_AVAILABLE}" -eq 0 ]; then
|
|
357
|
-
LOGIN_TIMEOUT="${SETUP_TUNNEL_LOGIN_TIMEOUT:-180}"
|
|
358
|
-
else
|
|
359
|
-
LOGIN_TIMEOUT="${SETUP_TUNNEL_LOGIN_TIMEOUT:-20}"
|
|
360
|
-
fi
|
|
234
|
+
# regardless of --origincert, so watch the canonical location. The wait is
|
|
235
|
+
# human-paced: the operator must click the zone row + Authorize on the VNC.
|
|
236
|
+
# 180 s default budget; SETUP_TUNNEL_LOGIN_TIMEOUT overrides for testing.
|
|
237
|
+
# The 2-second heartbeat inside the loop is the observability contract —
|
|
238
|
+
# no form-spawned script is allowed a silent poll of more than ~2 s.
|
|
239
|
+
LOGIN_TIMEOUT="${SETUP_TUNNEL_LOGIN_TIMEOUT:-180}"
|
|
361
240
|
LOGIN_WAIT=0
|
|
362
241
|
while [ ! -f "${HOME}/.cloudflared/cert.pem" ]; do
|
|
363
242
|
if ! kill -0 "${CF_PIPELINE_PID}" 2>/dev/null; then
|
|
@@ -22,7 +22,7 @@ Any Cloudflare action outside these four surfaces is a discipline violation —
|
|
|
22
22
|
|
|
23
23
|
Use this when the operator wants Cloudflare set up (or re-set up) end-to-end on the device. The script handles OAuth login, tunnel creation, DNS routing for each subdomain, config.yml + tunnel.state, and dispatches the `${BRAND}.service` restart to a transient `systemd-run` unit (Task 558) — all in one invocation. The restart fires a few seconds after the script exits so the script does not kill its own cgroup when invoked via the Bash tool; the chat UI receives a `server_shutdown` SSE frame and reconnects automatically. Post-restart hostname verification is out of scope for the script (connector is not up when the script exits) — verify via the next admin turn or manually with `curl -I https://<hostname>`. Apex hostnames cannot be routed by the CLI; when one is passed, the script prints an `ACTION REQUIRED` block naming the exact dashboard record to edit.
|
|
24
24
|
|
|
25
|
-
Step 1's OAuth flow is a state machine over
|
|
25
|
+
Step 1's OAuth flow is a state machine over two observable variables: the brand-scoped cert path (`${CFG_DIR}/cert.pem`) and the OAuth-default cert path (`~/.cloudflared/cert.pem`). When the brand-scoped cert is missing but the default-path cert is present from any prior partial run, the wrapper promotes it (`mv`) and emits `step=oauth-login result=ok reason=cert-promoted-from-default-path` without re-spawning cloudflared. When both are missing, the wrapper spawns `cloudflared tunnel login`, extracts the argotunnel URL from its stdout, and the instant the URL surfaces, mechanically opens it on the Pi's VNC chromium (`DISPLAY=${DISPLAY:-:99} /usr/bin/chromium <url> &`) — emitting `step=browser-spawn result=ok` and `step=browser-drive mode=operator-click`. The operator clicks the zone row + Authorize on the VNC; cloudflared's callback writes `~/.cloudflared/cert.pem`; the wrapper's cert-poll (180 s budget) picks it up and `mv`s it to the brand-scoped path. There is no CDP auto-click, no DOM matcher, no consent-page driver — the wrapper's job is to faithfully relay `cloudflared tunnel login`, never to layer automation on top (Task 858, doctrine `feedback_wrappers_faithfully_relay_third_party_cli`).
|
|
26
26
|
|
|
27
27
|
### How inputs reach the script
|
|
28
28
|
|
|
@@ -52,7 +52,7 @@ The agent does not invoke the script directly during onboarding — the endpoint
|
|
|
52
52
|
|
|
53
53
|
The endpoint returns `{ ok: false, field: "script", message, output }` and the form surfaces the error inline. Relay the output to the user, name the exit code, and cite `references/reset-guide.md` for the next action. Offer to re-render the form after any manual steps the script's error output named. Do not attempt a second invocation outside the form, a Playwright-driven dashboard inspection, or an alternative `cloudflared` command sequence. The discipline rule below applies.
|
|
54
54
|
|
|
55
|
-
When the failure reason is `
|
|
55
|
+
When the failure reason is `timeout-waiting-cert` (Task 858 — operator did not click Authorize within the 180 s budget), the form surfaces the timeout and the operator can re-submit. The page is still on the Pi VNC; the operator can click Authorize there and the next form submit will complete via the cert-promotion pre-flight (the cert lands in `~/.cloudflared/cert.pem` after consent, and the wrapper's `mv` runs on the next invocation). Do not suggest `~/reset-tunnel.sh` — the cert path is intact and a fresh attempt is the only remediation needed.
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
@@ -35,7 +35,7 @@ Activate when the user asks about WhatsApp — connecting, linking, checking sta
|
|
|
35
35
|
|
|
36
36
|
## Conversation Browsing
|
|
37
37
|
|
|
38
|
-
Three tools for reading WhatsApp conversation context. Messages come from the in-memory
|
|
38
|
+
Three tools for reading WhatsApp conversation context. Messages come from the in-memory ring buffer (recent inbound, ~500 per JID, ephemeral). The graph carries the durable record — see "Live persistence" below.
|
|
39
39
|
|
|
40
40
|
| Tool | Returns |
|
|
41
41
|
|------|---------|
|
|
@@ -43,7 +43,9 @@ Three tools for reading WhatsApp conversation context. Messages come from the in
|
|
|
43
43
|
| `whatsapp-messages` | Stored messages for a specific JID — sender, body, timestamp, reply context. Supports optional `limit` for most recent N messages |
|
|
44
44
|
| `whatsapp-group-info` | Group metadata from WhatsApp servers — subject, description, participants with admin flags, creation date. Requires active connection |
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
## Live persistence (Task 857)
|
|
47
|
+
|
|
48
|
+
Every `messages.upsert` event (both `notify` and `append`, both `fromMe` directions) writes a `:Message:WhatsAppMessage` row to Neo4j attached to the sessionKey-keyed `:Conversation`. A single capture site at `platform/ui/app/lib/whatsapp/manager.ts` covers inbound, outbound (Baileys echoes agent-sent messages back through `messages.upsert` with `fromMe=true`), and owner-mirror — without touching `outbound/send.ts`. `messageId` namespace is `whatsapp-live:<accountId>:<remoteJid>:<msg.key.id>`, collision-free with the `whatsapp-export:` namespace used by the offline `whatsapp-import` plugin. Persist failures are loud (`[whatsapp-persist] FAIL …`) and never block dispatch — silent loss is the worse failure mode.
|
|
47
49
|
|
|
48
50
|
## Skills
|
|
49
51
|
|