@rubytech/create-maxy 1.0.802 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.802",
3
+ "version": "1.0.803",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -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 588 drives the Cloudflare Authorize click via CDP WebSocket (`_cdp-authorize.mjs`, below) no human click required when the VNC browser is already signed into Cloudflare. The OAuth cert-wait after the click is bounded at 20 s with a 2-second `step=oauth-login result=awaiting-cert elapsed=<N>s` heartbeat so no poll exceeds ~2 s silent. |
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
- ### Three states the consent page can render
134
+ ### States the consent page can render
135
135
 
136
- The dashboard at `dash.cloudflare.com/argotunnel?...` shows one of three states. The automation script's CDP helper (`_cdp-authorize.mjs`) detects each one; the same triage applies when you walk the flow manually.
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.** This is the normal first run. Click the zone you want, then click **Authorize**. Cloudflare's callback fires and `~/.cloudflared/cert.pem` lands a second or two later. Continue with the `mv` above.
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. **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`.
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
- 3. **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.
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
- # Task 556: Step 1 owns the browser-spawn deterministically. Instead of
18
- # delegating to cloudflared's xdg-open (which silently degrades to "print
19
- # URL and wait"), the script drives Chromium on :99 via CDP `PUT /json/new?<url>`
20
- # on http://127.0.0.1:9222the same mechanism
21
- # /api/admin/device-browser/navigate uses. cloudflared's stdout+stderr is
22
- # teed line-by-line into STREAM_LOG_PATH so the chat UI's server-side
23
- # tailer renders live progress in-turn.
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 three observable variables — brand-scoped
66
- # cert path, default cert path, and the helper's outcome (Task 855):
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 the
74
- # helper. Idempotent.
75
- # both missing → spawn cloudflared, drive
76
- # the consent page via CDP,
77
- # poll for cert.pem, mv.
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 556 / 588 / 855):
80
- # 1. CDP precheck on 127.0.0.1:9222 — loud failure if Chromium isn't up.
81
- # 2. Spawn cloudflared with stdout+stderr teed line-by-line to
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
- # 3. Extract the authorize URL with a tolerant regex as it streams.
85
- # 4. Drive the VNC Chromium to that URL via CDP `PUT /json/new?<url>`.
86
- # 5. Run _cdp-authorize.mjs tri-state matcher returns one of:
87
- # reason=clicked → button matched + clicked
88
- # reason=cert-already-installed → Success modal detected
89
- # reason=authorize-button-not-found loud failure, exit 1
90
- # (a)+(b) drop into the cert-pem poll; (c) bubbles up.
91
- # 6. Wait for ~/.cloudflared/cert.pem to land with bounded timeout.
92
- # 7. Move cert.pem into the brand-scoped path.
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
- # Every failure branch exits 1 loudly naming the cause no silent retries,
95
- # no xdg-open race, no cloudflared-internal browser-spawn path.
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
- # Structured-looking enough to route to the "Authorise in Cloudflare"
214
- # button on Task 664's admin surface without interfering with the
215
- # existing cloudflared-line extraction (which scans the same URL on
216
- # stderr from the line above). Emitted before CDP so the button is
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
- # If CDP isn't available on this device, skip the auto-click branch
221
- # and fall through to the cert.pem poll. The operator's own browser
222
- # click (via ActionLogPanel's button) hits the same webhook; the
223
- # callback writes ~/.cloudflared/cert.pem the moment consent lands,
224
- # and the existing LOGIN_TIMEOUT loop below picks it up.
225
- if [ "${CDP_AVAILABLE}" -eq 0 ]; then
226
- phase_line setup-tunnel step=browser-drive mode=operator-click url="${AUTH_URL}"
227
- else
228
- # Drive CDP. Same PUT /json/new?<url> contract as
229
- # platform/ui/app/lib/cdp-client.ts (which uses encodeURIComponent on
230
- # the URL). Without percent-encoding, CDP's URL parser splits on the
231
- # inner `?` and `&` in the argotunnel URL and drops the callback/token
232
- # query params Chromium lands on a bare argotunnel page and the
233
- # consent screen is never reached. `jq -sRr @uri` percent-encodes a raw
234
- # string, matching the TS client exactly.
235
- AUTH_URL_ENC="$(printf '%s' "${AUTH_URL}" | jq -sRr @uri)"
236
- CDP_RESP="$(curl -sf --max-time 5 -X PUT "http://127.0.0.1:9222/json/new?${AUTH_URL_ENC}" 2>&1 || true)"
237
- if [ -z "${CDP_RESP}" ]; then
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. Task 588
349
- # collapses the pre-click 180 s human-latency window to a 20 s deterministic
350
- # round-trip window (Cloudflare cloudflared webhook after consent). The
351
- # 2-second heartbeat inside the loop is the observability contract for
352
- # this bounded wait — no form-spawned script is allowed a silent poll of
353
- # more than ~2 s per criterion 3 of Task 588.
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 three observable variables: the brand-scoped cert path (`${CFG_DIR}/cert.pem`), the OAuth-default cert path (`~/.cloudflared/cert.pem`), and the consent-page DOM via the CDP helper `_cdp-authorize.mjs` (Task 855). 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 helper polls `dash.cloudflare.com/argotunnel` and resolves to one of three terminal outcomes `reason=clicked` (button matched and clicked), `reason=cert-already-installed` (Success modal text detected the bound account already has a cert, callback is idempotent so the in-flight cloudflared subprocess writes the cert anyway), or `reason=authorize-button-not-found` (neither anchor matched; loud failure, exit 1). The first two paths drop into the existing brand-scoped cert poll; the third surfaces a remediation card in the form so the operator can complete the consent click in their own browser without SSH or `~/reset-tunnel.sh`.
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 `authorize-button-not-found` (Task 855), the form renders a verbatim quote of `references/dashboard-guide.md` § "Authorise a new tunnel (pick the right zone)" alongside a "Try again" button the operator completes the consent click in their own browser, then re-submits the same form. No agent action is required; relay the form's chat acknowledgement when it arrives. Do not suggest `~/reset-tunnel.sh` for this reason — the cert path is intact and a fresh consent click is the only remediation needed.
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
 
@@ -340,7 +340,7 @@ Please report this to https://github.com/markedjs/marked.`,e){let e=`<p>An error
340
340
  border: 1px solid rgba(0,0,0,0.1); }
341
341
  td { padding: 6px 10px; border: 1px solid rgba(0,0,0,0.1); }
342
342
  `;function _T({data:e,onSubmit:t,submitted:n,isStreaming:r}){let{title:a,columns:o,rows:s,filePath:c,brandName:l,brandLogo:u}=e??{},d=a??`Grid`,[f,p]=(0,B.useState)(o??[`Column 1`]),[m,h]=(0,B.useState)(()=>{let e=s??[[``]];return e.length>0?e:[[``]]}),[g,_]=(0,B.useState)(null),[v,y]=(0,B.useState)(``),b=(0,B.useRef)(null),[x,S]=(0,B.useState)(()=>(o??[`Column 1`]).map(()=>pT)),C=(0,B.useRef)(null),[T,E]=(0,B.useState)(`idle`),D=(0,B.useRef)(null),O=(0,B.useRef)(null),k=(0,B.useRef)(JSON.stringify({columns:o,rows:s}));(0,B.useEffect)(()=>{let e=JSON.stringify({columns:o,rows:s});e!==k.current&&(k.current=e,o&&p(o),s&&(h(s.length>0?s:[[``]]),S(e=>{let t=o?.length??e.length;return t>e.length?[...e,...Array(t-e.length).fill(pT)]:e.slice(0,t)})))},[o,s]);let A=(0,B.useCallback)(()=>{D.current&&clearTimeout(D.current),D.current=setTimeout(()=>{E(`saving`),t(JSON.stringify({action:`save`,columns:f,rows:m,...c?{filePath:c}:{}})),O.current&&clearTimeout(O.current),E(`saved`),O.current=setTimeout(()=>E(`idle`),2e3)},500)},[f,m,c,t]);(0,B.useEffect)(()=>()=>{D.current&&clearTimeout(D.current),O.current&&clearTimeout(O.current)},[]);let j=Uw({title:d,isStreaming:r,channelName:`maxy-grid-editor`,renderPopout:(0,B.useCallback)((e,t)=>{let n=e.document;n.open(),n.close(),n.title=t;let r=n.createElement(`style`);r.textContent=gT,n.head.appendChild(r);let i=n.createElement(`div`);i.className=`title`,i.textContent=t,n.body.appendChild(i);let a=n.createElement(`table`),o=n.createElement(`thead`),s=n.createElement(`tr`);for(let e of f){let t=n.createElement(`th`);t.textContent=e,s.appendChild(t)}o.appendChild(s),a.appendChild(o);let c=n.createElement(`tbody`);for(let e of m){let t=n.createElement(`tr`);for(let r of e){let e=n.createElement(`td`);e.textContent=r,t.appendChild(e)}c.appendChild(t)}a.appendChild(c),n.body.appendChild(a)},[f,m])}),M=Ww({onSubmit:t}),N=(0,B.useCallback)((e,t)=>{_({row:e,col:t}),y(m[e]?.[t]??``),setTimeout(()=>b.current?.focus(),0)},[m]),P=(0,B.useCallback)(()=>{if(!g)return;let{row:e,col:t}=g;h(n=>{let r=n.map(e=>[...e]);return r[e]&&(r[e][t]=v),r}),_(null),A()},[g,v,A]),ee=(0,B.useCallback)(()=>{_(null)},[]),te=(0,B.useCallback)(e=>{if(g)if(e.key===`Tab`){e.preventDefault(),P();let{row:t,col:n}=g,r=n+1;if(r<f.length)N(t,r);else{let e=t+1;e>=m.length&&h(e=>[...e,Array(f.length).fill(``)]),N(Math.min(e,m.length),0)}}else if(e.key===`Enter`){e.preventDefault(),P();let{row:t,col:n}=g,r=t+1;r>=m.length&&h(e=>[...e,Array(f.length).fill(``)]),N(Math.min(r,m.length),n)}else e.key===`Escape`&&ee()},[g,f.length,m.length,P,N,ee]),ne=(0,B.useCallback)(()=>{h(e=>[...e,Array(f.length).fill(``)]),A()},[f.length,A]),F=(0,B.useCallback)(e=>{h(t=>t.length<=1?[Array(f.length).fill(``)]:t.filter((t,n)=>n!==e)),g?.row===e&&_(null),A()},[f.length,g,A]),I=(0,B.useCallback)(e=>{let t=e.clipboardData.getData(`text/plain`);if(!t||!g)return;let n=t.split(`
343
- `).filter(e=>e.trim());if(n.length<=1&&!n[0]?.includes(` `)&&!n[0]?.includes(`,`))return;e.preventDefault();let r=n.slice(0,dT).map(mT);n.length>dT&&console.warn(`Grid editor: paste truncated from ${n.length} to ${dT} rows`);let{row:i,col:a}=g;h(e=>{let t=e.map(e=>[...e]);for(let e=0;e<r.length;e++){let n=i+e;for(;n>=t.length;)t.push(Array(f.length).fill(``));for(let i=0;i<r[e].length;i++){let o=a+i;o<f.length&&(t[n][o]=r[e][i])}}return t}),A()},[g,f.length,A]),re=(0,B.useCallback)(()=>{let e=hT(f,m),t=new Blob([e],{type:`text/csv;charset=utf-8;`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`${d.replace(/[^a-zA-Z0-9]/g,`_`)}.csv`,r.click(),URL.revokeObjectURL(n)},[f,m,d]),ie=(0,B.useCallback)(()=>{$w({title:d,content:eT(f,m),brandName:l,brandLogo:u})},[d,f,m,l,u]),ae=(0,B.useCallback)((e,t)=>{t.preventDefault(),C.current={colIndex:e,startX:t.clientX,startWidth:x[e]};let n=null,r=e=>{C.current&&(n&&cancelAnimationFrame(n),n=requestAnimationFrame(()=>{if(!C.current)return;let t=e.clientX-C.current.startX,n=Math.max(fT,C.current.startWidth+t);S(e=>{let t=[...e];return t[C.current.colIndex]=n,t})}))},i=()=>{C.current=null,n&&cancelAnimationFrame(n),window.removeEventListener(`mousemove`,r),window.removeEventListener(`mouseup`,i)};window.addEventListener(`mousemove`,r),window.addEventListener(`mouseup`,i)},[x]),se=(0,B.useCallback)(()=>{g&&M.openCommentInput()},[g,M]),ce=(0,B.useCallback)(()=>{if(!g)return;let e=m[g.row]?.[g.col]??``;M.submitComment(e,{row:g.row,column:g.col})},[g,m,M]);if(n)return(0,V.jsxs)(`div`,{className:`editor-base grid-editor grid-editor--submitted`,children:[(0,V.jsxs)(`div`,{className:`editor-base__bar`,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),(0,V.jsx)(`div`,{className:`editor-base__actions`,children:(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Submitted`})})]}),(0,V.jsx)(`div`,{className:`grid-editor__body`,children:(0,V.jsx)(`div`,{className:`grid-editor__table-wrap`,children:(0,V.jsxs)(`table`,{className:`grid-editor__table`,role:`grid`,"aria-label":d,children:[(0,V.jsx)(`thead`,{children:(0,V.jsx)(`tr`,{children:f.map((e,t)=>(0,V.jsx)(`th`,{className:`grid-editor__col-header`,style:{width:x[t]},children:e},t))})}),(0,V.jsx)(`tbody`,{children:m.map((e,t)=>(0,V.jsx)(`tr`,{children:e.map((e,t)=>(0,V.jsx)(`td`,{className:`grid-editor__cell`,role:`gridcell`,style:{width:x[t]},children:(0,V.jsx)(`span`,{className:`grid-editor__cell-text`,children:e})},t))},t))})]})})})]});let le=typeof window<`u`&&window.innerWidth<640,ue=typeof BroadcastChannel<`u`,de=(0,V.jsxs)(`div`,{className:`editor-base__actions`,children:[T===`saving`&&(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Saving...`}),T===`saved`&&(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Saved`}),M.pendingComments.length>0&&(0,V.jsxs)(w,{variant:`ghost`,size:`sm`,onClick:M.sendComments,children:[`Send `,M.pendingComments.length,` comment`,M.pendingComments.length>1?`s`:``]}),g&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:$e,onClick:se,"aria-label":`Comment on cell`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:L,onClick:re,"aria-label":`Download CSV`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Le,onClick:ie,"aria-label":`Export PDF`}),!le&&ue&&!j.poppedOut&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Fe,onClick:j.handlePopout,"aria-label":`Pop out`}),!j.fullscreen&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Qe,onClick:j.handleFullscreen,"aria-label":`Full screen`}),j.fullscreen&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:nt,onClick:j.exitFullscreen,"aria-label":`Exit full screen`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:rt,onClick:j.handleMinimise,"aria-label":`Minimise`})]}),fe=(0,V.jsxs)(`div`,{className:`grid-editor__table-wrap`,onPaste:I,children:[M.showCommentInput?(0,V.jsxs)(`div`,{className:`grid-editor__comment-popover`,children:[(0,V.jsx)(`input`,{ref:M.commentInputRef,type:`text`,value:M.commentText,onChange:e=>M.setCommentText(e.target.value),onKeyDown:e=>{e.key===`Enter`&&ce(),e.key===`Escape`&&M.cancelComment()},placeholder:`Add a comment...`,className:`doc-editor__comment-input`,"aria-label":`Add comment on cell`}),(0,V.jsxs)(`div`,{className:`doc-editor__comment-actions`,children:[(0,V.jsx)(w,{variant:`primary`,size:`sm`,onClick:ce,disabled:!M.commentText.trim(),children:`Add`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:oe,onClick:M.cancelComment,"aria-label":`Cancel`})]})]}):null,(0,V.jsxs)(`table`,{className:`grid-editor__table`,role:`grid`,"aria-label":d,children:[(0,V.jsx)(`thead`,{children:(0,V.jsxs)(`tr`,{children:[(0,V.jsx)(`th`,{className:`grid-editor__row-header`}),f.map((e,t)=>(0,V.jsxs)(`th`,{className:`grid-editor__col-header`,style:{width:x[t]},children:[e,(0,V.jsx)(`div`,{className:`grid-editor__resize-handle`,onMouseDown:e=>ae(t,e),role:`separator`,"aria-orientation":`vertical`})]},t))]})}),(0,V.jsx)(`tbody`,{children:m.map((e,t)=>(0,V.jsxs)(`tr`,{children:[(0,V.jsxs)(`td`,{className:`grid-editor__row-header`,children:[(0,V.jsx)(`span`,{className:`grid-editor__row-num`,children:t+1}),m.length>1&&(0,V.jsx)(`button`,{className:`grid-editor__row-delete`,onClick:()=>F(t),"aria-label":`Delete row ${t+1}`,children:(0,V.jsx)(i,{size:12})})]}),e.map((e,n)=>{let r=g?.row===t&&g?.col===n;return(0,V.jsx)(`td`,{className:`grid-editor__cell ${r?`grid-editor__cell--active`:``}`,onClick:()=>!r&&N(t,n),role:`gridcell`,"aria-selected":r,style:{width:x[n]},children:r?(0,V.jsx)(`input`,{ref:b,type:`text`,className:`grid-editor__cell-input`,value:v,onChange:e=>y(e.target.value),onKeyDown:te,onBlur:P,"aria-label":`${f[n]} row ${t+1}`}):(0,V.jsx)(`span`,{className:`grid-editor__cell-text`,children:e})},n)})]},t))})]}),(0,V.jsxs)(`button`,{className:`grid-editor__add-row`,onClick:ne,"aria-label":`Add row`,children:[(0,V.jsx)(Oe,{size:14}),(0,V.jsx)(`span`,{children:`Add row`})]})]});return j.fullscreen?(0,V.jsxs)(`div`,{className:`editor-base-fullscreen`,children:[(0,V.jsxs)(`div`,{className:`editor-base-fullscreen__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base-fullscreen__title`,children:d}),de]}),(0,V.jsx)(`div`,{className:`editor-base-fullscreen__body`,style:{maxWidth:`none`},children:fe})]}):j.poppedOut?(0,V.jsx)(`div`,{className:`editor-base editor-base--popout`,children:(0,V.jsxs)(`div`,{className:`editor-base__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),(0,V.jsxs)(`div`,{className:`editor-base__actions`,children:[(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Popped out`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:dt,onClick:j.handlePopIn,"aria-label":`Pop back in`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:rt,onClick:j.handleMinimise,"aria-label":`Close`})]})]})}):j.minimised?(0,V.jsx)(`div`,{className:`editor-base editor-base--minimised`,onClick:j.handleExpand,role:`button`,tabIndex:0,onKeyDown:e=>e.key===`Enter`&&j.handleExpand(),children:(0,V.jsxs)(`div`,{className:`editor-base__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),(0,V.jsxs)(`div`,{className:`editor-base__actions`,onClick:e=>e.stopPropagation(),children:[!le&&ue&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Fe,onClick:j.handlePopout,"aria-label":`Pop out`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Qe,onClick:j.handleFullscreen,"aria-label":`Full screen`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:ct,onClick:j.handleExpand,"aria-label":`Expand`})]})]})}):(0,V.jsxs)(`div`,{className:`editor-base grid-editor`,children:[(0,V.jsxs)(`div`,{className:`editor-base__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),de]}),(0,V.jsx)(`div`,{className:`grid-editor__body`,children:fe})]})}var vT=Object.defineProperty,yT=Object.getOwnPropertySymbols,bT=Object.prototype.hasOwnProperty,xT=Object.prototype.propertyIsEnumerable,ST=(e,t,n)=>t in e?vT(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,CT=(e,t)=>{for(var n in t||={})bT.call(t,n)&&ST(e,n,t[n]);if(yT)for(var n of yT(t))xT.call(t,n)&&ST(e,n,t[n]);return e},wT=(e,t)=>{var n={};for(var r in e)bT.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&yT)for(var r of yT(e))t.indexOf(r)<0&&xT.call(e,r)&&(n[r]=e[r]);return n},TT;(e=>{let t=class t{constructor(e,n,r,a){if(this.version=e,this.errorCorrectionLevel=n,this.modules=[],this.isFunction=[],e<t.MIN_VERSION||e>t.MAX_VERSION)throw RangeError(`Version value out of range`);if(a<-1||a>7)throw RangeError(`Mask value out of range`);this.size=e*4+17;let o=[];for(let e=0;e<this.size;e++)o.push(!1);for(let e=0;e<this.size;e++)this.modules.push(o.slice()),this.isFunction.push(o.slice());this.drawFunctionPatterns();let s=this.addEccAndInterleave(r);if(this.drawCodewords(s),a==-1){let e=1e9;for(let t=0;t<8;t++){this.applyMask(t),this.drawFormatBits(t);let n=this.getPenaltyScore();n<e&&(a=t,e=n),this.applyMask(t)}}i(0<=a&&a<=7),this.mask=a,this.applyMask(a),this.drawFormatBits(a),this.isFunction=[]}static encodeText(n,r){let i=e.QrSegment.makeSegments(n);return t.encodeSegments(i,r)}static encodeBinary(n,r){let i=e.QrSegment.makeBytes(n);return t.encodeSegments([i],r)}static encodeSegments(e,r,a=1,s=40,c=-1,l=!0){if(!(t.MIN_VERSION<=a&&a<=s&&s<=t.MAX_VERSION)||c<-1||c>7)throw RangeError(`Invalid value`);let u,d;for(u=a;;u++){let n=t.getNumDataCodewords(u,r)*8,i=o.getTotalBits(e,u);if(i<=n){d=i;break}if(u>=s)throw RangeError(`Data too long`)}for(let e of[t.Ecc.MEDIUM,t.Ecc.QUARTILE,t.Ecc.HIGH])l&&d<=t.getNumDataCodewords(u,e)*8&&(r=e);let f=[];for(let t of e){n(t.mode.modeBits,4,f),n(t.numChars,t.mode.numCharCountBits(u),f);for(let e of t.getData())f.push(e)}i(f.length==d);let p=t.getNumDataCodewords(u,r)*8;i(f.length<=p),n(0,Math.min(4,p-f.length),f),n(0,(8-f.length%8)%8,f),i(f.length%8==0);for(let e=236;f.length<p;e^=253)n(e,8,f);let m=[];for(;m.length*8<f.length;)m.push(0);return f.forEach((e,t)=>m[t>>>3]|=e<<7-(t&7)),new t(u,r,m,c)}getModule(e,t){return 0<=e&&e<this.size&&0<=t&&t<this.size&&this.modules[t][e]}getModules(){return this.modules}drawFunctionPatterns(){for(let e=0;e<this.size;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);let e=this.getAlignmentPatternPositions(),t=e.length;for(let n=0;n<t;n++)for(let r=0;r<t;r++)n==0&&r==0||n==0&&r==t-1||n==t-1&&r==0||this.drawAlignmentPattern(e[n],e[r]);this.drawFormatBits(0),this.drawVersion()}drawFormatBits(e){let t=this.errorCorrectionLevel.formatBits<<3|e,n=t;for(let e=0;e<10;e++)n=n<<1^(n>>>9)*1335;let a=(t<<10|n)^21522;i(a>>>15==0);for(let e=0;e<=5;e++)this.setFunctionModule(8,e,r(a,e));this.setFunctionModule(8,7,r(a,6)),this.setFunctionModule(8,8,r(a,7)),this.setFunctionModule(7,8,r(a,8));for(let e=9;e<15;e++)this.setFunctionModule(14-e,8,r(a,e));for(let e=0;e<8;e++)this.setFunctionModule(this.size-1-e,8,r(a,e));for(let e=8;e<15;e++)this.setFunctionModule(8,this.size-15+e,r(a,e));this.setFunctionModule(8,this.size-8,!0)}drawVersion(){if(this.version<7)return;let e=this.version;for(let t=0;t<12;t++)e=e<<1^(e>>>11)*7973;let t=this.version<<12|e;i(t>>>18==0);for(let e=0;e<18;e++){let n=r(t,e),i=this.size-11+e%3,a=Math.floor(e/3);this.setFunctionModule(i,a,n),this.setFunctionModule(a,i,n)}}drawFinderPattern(e,t){for(let n=-4;n<=4;n++)for(let r=-4;r<=4;r++){let i=Math.max(Math.abs(r),Math.abs(n)),a=e+r,o=t+n;0<=a&&a<this.size&&0<=o&&o<this.size&&this.setFunctionModule(a,o,i!=2&&i!=4)}}drawAlignmentPattern(e,t){for(let n=-2;n<=2;n++)for(let r=-2;r<=2;r++)this.setFunctionModule(e+r,t+n,Math.max(Math.abs(r),Math.abs(n))!=1)}setFunctionModule(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}addEccAndInterleave(e){let n=this.version,r=this.errorCorrectionLevel;if(e.length!=t.getNumDataCodewords(n,r))throw RangeError(`Invalid argument`);let a=t.NUM_ERROR_CORRECTION_BLOCKS[r.ordinal][n],o=t.ECC_CODEWORDS_PER_BLOCK[r.ordinal][n],s=Math.floor(t.getNumRawDataModules(n)/8),c=a-s%a,l=Math.floor(s/a),u=[],d=t.reedSolomonComputeDivisor(o);for(let n=0,r=0;n<a;n++){let i=e.slice(r,r+l-o+(n<c?0:1));r+=i.length;let a=t.reedSolomonComputeRemainder(i,d);n<c&&i.push(0),u.push(i.concat(a))}let f=[];for(let e=0;e<u[0].length;e++)u.forEach((t,n)=>{(e!=l-o||n>=c)&&f.push(t[e])});return i(f.length==s),f}drawCodewords(e){if(e.length!=Math.floor(t.getNumRawDataModules(this.version)/8))throw RangeError(`Invalid argument`);let n=0;for(let t=this.size-1;t>=1;t-=2){t==6&&(t=5);for(let i=0;i<this.size;i++)for(let a=0;a<2;a++){let o=t-a,s=t+1&2?i:this.size-1-i;!this.isFunction[s][o]&&n<e.length*8&&(this.modules[s][o]=r(e[n>>>3],7-(n&7)),n++)}}i(n==e.length*8)}applyMask(e){if(e<0||e>7)throw RangeError(`Mask value out of range`);for(let t=0;t<this.size;t++)for(let n=0;n<this.size;n++){let r;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error(`Unreachable`)}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}getPenaltyScore(){let e=0;for(let n=0;n<this.size;n++){let r=!1,i=0,a=[0,0,0,0,0,0,0];for(let o=0;o<this.size;o++)this.modules[n][o]==r?(i++,i==5?e+=t.PENALTY_N1:i>5&&e++):(this.finderPenaltyAddHistory(i,a),r||(e+=this.finderPenaltyCountPatterns(a)*t.PENALTY_N3),r=this.modules[n][o],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,a)*t.PENALTY_N3}for(let n=0;n<this.size;n++){let r=!1,i=0,a=[0,0,0,0,0,0,0];for(let o=0;o<this.size;o++)this.modules[o][n]==r?(i++,i==5?e+=t.PENALTY_N1:i>5&&e++):(this.finderPenaltyAddHistory(i,a),r||(e+=this.finderPenaltyCountPatterns(a)*t.PENALTY_N3),r=this.modules[o][n],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,a)*t.PENALTY_N3}for(let n=0;n<this.size-1;n++)for(let r=0;r<this.size-1;r++){let i=this.modules[n][r];i==this.modules[n][r+1]&&i==this.modules[n+1][r]&&i==this.modules[n+1][r+1]&&(e+=t.PENALTY_N2)}let n=0;for(let e of this.modules)n=e.reduce((e,t)=>e+(t?1:0),n);let r=this.size*this.size,a=Math.ceil(Math.abs(n*20-r*10)/r)-1;return i(0<=a&&a<=9),e+=a*t.PENALTY_N4,i(0<=e&&e<=2568888),e}getAlignmentPatternPositions(){if(this.version==1)return[];{let e=Math.floor(this.version/7)+2,t=this.version==32?26:Math.ceil((this.version*4+4)/(e*2-2))*2,n=[6];for(let r=this.size-7;n.length<e;r-=t)n.splice(1,0,r);return n}}static getNumRawDataModules(e){if(e<t.MIN_VERSION||e>t.MAX_VERSION)throw RangeError(`Version number out of range`);let n=(16*e+128)*e+64;if(e>=2){let t=Math.floor(e/7)+2;n-=(25*t-10)*t-55,e>=7&&(n-=36)}return i(208<=n&&n<=29648),n}static getNumDataCodewords(e,n){return Math.floor(t.getNumRawDataModules(e)/8)-t.ECC_CODEWORDS_PER_BLOCK[n.ordinal][e]*t.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][e]}static reedSolomonComputeDivisor(e){if(e<1||e>255)throw RangeError(`Degree out of range`);let n=[];for(let t=0;t<e-1;t++)n.push(0);n.push(1);let r=1;for(let i=0;i<e;i++){for(let e=0;e<n.length;e++)n[e]=t.reedSolomonMultiply(n[e],r),e+1<n.length&&(n[e]^=n[e+1]);r=t.reedSolomonMultiply(r,2)}return n}static reedSolomonComputeRemainder(e,n){let r=n.map(e=>0);for(let i of e){let e=i^r.shift();r.push(0),n.forEach((n,i)=>r[i]^=t.reedSolomonMultiply(n,e))}return r}static reedSolomonMultiply(e,t){if(e>>>8||t>>>8)throw RangeError(`Byte out of range`);let n=0;for(let r=7;r>=0;r--)n=n<<1^(n>>>7)*285,n^=(t>>>r&1)*e;return i(n>>>8==0),n}finderPenaltyCountPatterns(e){let t=e[1];i(t<=this.size*3);let n=t>0&&e[2]==t&&e[3]==t*3&&e[4]==t&&e[5]==t;return(n&&e[0]>=t*4&&e[6]>=t?1:0)+(n&&e[6]>=t*4&&e[0]>=t?1:0)}finderPenaltyTerminateAndCount(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),t+=this.size,this.finderPenaltyAddHistory(t,n),this.finderPenaltyCountPatterns(n)}finderPenaltyAddHistory(e,t){t[0]==0&&(e+=this.size),t.pop(),t.unshift(e)}};t.MIN_VERSION=1,t.MAX_VERSION=40,t.PENALTY_N1=3,t.PENALTY_N2=3,t.PENALTY_N3=40,t.PENALTY_N4=10,t.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],t.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],e.QrCode=t;function n(e,t,n){if(t<0||t>31||e>>>t)throw RangeError(`Value out of range`);for(let r=t-1;r>=0;r--)n.push(e>>>r&1)}function r(e,t){return(e>>>t&1)!=0}function i(e){if(!e)throw Error(`Assertion error`)}let a=class e{constructor(e,t,n){if(this.mode=e,this.numChars=t,this.bitData=n,t<0)throw RangeError(`Invalid argument`);this.bitData=n.slice()}static makeBytes(t){let r=[];for(let e of t)n(e,8,r);return new e(e.Mode.BYTE,t.length,r)}static makeNumeric(t){if(!e.isNumeric(t))throw RangeError(`String contains non-numeric characters`);let r=[];for(let e=0;e<t.length;){let i=Math.min(t.length-e,3);n(parseInt(t.substring(e,e+i),10),i*3+1,r),e+=i}return new e(e.Mode.NUMERIC,t.length,r)}static makeAlphanumeric(t){if(!e.isAlphanumeric(t))throw RangeError(`String contains unencodable characters in alphanumeric mode`);let r=[],i;for(i=0;i+2<=t.length;i+=2){let a=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(i))*45;a+=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(i+1)),n(a,11,r)}return i<t.length&&n(e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(i)),6,r),new e(e.Mode.ALPHANUMERIC,t.length,r)}static makeSegments(t){return t==``?[]:e.isNumeric(t)?[e.makeNumeric(t)]:e.isAlphanumeric(t)?[e.makeAlphanumeric(t)]:[e.makeBytes(e.toUtf8ByteArray(t))]}static makeEci(t){let r=[];if(t<0)throw RangeError(`ECI assignment value out of range`);if(t<128)n(t,8,r);else if(t<16384)n(2,2,r),n(t,14,r);else if(t<1e6)n(6,3,r),n(t,21,r);else throw RangeError(`ECI assignment value out of range`);return new e(e.Mode.ECI,0,r)}static isNumeric(t){return e.NUMERIC_REGEX.test(t)}static isAlphanumeric(t){return e.ALPHANUMERIC_REGEX.test(t)}getData(){return this.bitData.slice()}static getTotalBits(e,t){let n=0;for(let r of e){let e=r.mode.numCharCountBits(t);if(r.numChars>=1<<e)return 1/0;n+=4+e+r.bitData.length}return n}static toUtf8ByteArray(e){e=encodeURI(e);let t=[];for(let n=0;n<e.length;n++)e.charAt(n)==`%`?(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2):t.push(e.charCodeAt(n));return t}};a.NUMERIC_REGEX=/^[0-9]*$/,a.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,a.ALPHANUMERIC_CHARSET=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`;let o=a;e.QrSegment=a})(TT||={}),(e=>{(e=>{let t=class{constructor(e,t){this.ordinal=e,this.formatBits=t}};t.LOW=new t(0,1),t.MEDIUM=new t(1,0),t.QUARTILE=new t(2,3),t.HIGH=new t(3,2),e.Ecc=t})(e.QrCode||={})})(TT||={}),(e=>{(e=>{let t=class{constructor(e,t){this.modeBits=e,this.numBitsCharCount=t}numCharCountBits(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}};t.NUMERIC=new t(1,[10,12,14]),t.ALPHANUMERIC=new t(2,[9,11,13]),t.BYTE=new t(4,[8,16,16]),t.KANJI=new t(8,[8,10,12]),t.ECI=new t(7,[0,0,0]),e.Mode=t})(e.QrSegment||={})})(TT||={});var ET=TT,DT={L:ET.QrCode.Ecc.LOW,M:ET.QrCode.Ecc.MEDIUM,Q:ET.QrCode.Ecc.QUARTILE,H:ET.QrCode.Ecc.HIGH},OT=128,kT=`L`,AT=`#FFFFFF`,jT=`#000000`,MT=!1,NT=1,PT=4,FT=0,IT=.1;function LT(e,t=0){let n=[];return e.forEach(function(e,r){let i=null;e.forEach(function(a,o){if(!a&&i!==null){n.push(`M${i+t} ${r+t}h${o-i}v1H${i+t}z`),i=null;return}if(o===e.length-1){if(!a)return;i===null?n.push(`M${o+t},${r+t} h1v1H${o+t}z`):n.push(`M${i+t},${r+t} h${o+1-i}v1H${i+t}z`);return}a&&i===null&&(i=o)})}),n.join(``)}function RT(e,t){return e.slice().map((e,n)=>n<t.y||n>=t.y+t.h?e:e.map((e,n)=>n<t.x||n>=t.x+t.w?e:!1))}function zT(e,t,n,r){if(r==null)return null;let i=e.length+n*2,a=Math.floor(t*IT),o=i/t,s=(r.width||a)*o,c=(r.height||a)*o,l=r.x==null?e.length/2-s/2:r.x*o,u=r.y==null?e.length/2-c/2:r.y*o,d=r.opacity==null?1:r.opacity,f=null;if(r.excavate){let e=Math.floor(l),t=Math.floor(u);f={x:e,y:t,w:Math.ceil(s+l-e),h:Math.ceil(c+u-t)}}let p=r.crossOrigin;return{x:l,y:u,h:c,w:s,excavation:f,opacity:d,crossOrigin:p}}function BT(e,t){return t==null?e?PT:FT:Math.max(Math.floor(t),0)}function VT({value:e,level:t,minVersion:n,includeMargin:r,marginSize:i,imageSettings:a,size:o,boostLevel:s}){let c=B.useMemo(()=>{let r=(Array.isArray(e)?e:[e]).reduce((e,t)=>(e.push(...ET.QrSegment.makeSegments(t)),e),[]);return ET.QrCode.encodeSegments(r,DT[t],n,void 0,void 0,s)},[e,t,n,s]),{cells:l,margin:u,numCells:d,calculatedImageSettings:f}=B.useMemo(()=>{let e=c.getModules(),t=BT(r,i);return{cells:e,margin:t,numCells:e.length+t*2,calculatedImageSettings:zT(e,o,t,a)}},[c,o,a,r,i]);return{qrcode:c,margin:u,cells:l,numCells:d,calculatedImageSettings:f}}var HT=function(){try{new Path2D().addPath(new Path2D)}catch{return!1}return!0}(),UT=B.forwardRef(function(e,t){let n=e,{value:r,size:i=OT,level:a=kT,bgColor:o=AT,fgColor:s=jT,includeMargin:c=MT,minVersion:l=NT,boostLevel:u,marginSize:d,imageSettings:f}=n,p=wT(n,[`value`,`size`,`level`,`bgColor`,`fgColor`,`includeMargin`,`minVersion`,`boostLevel`,`marginSize`,`imageSettings`]),{style:m}=p,h=wT(p,[`style`]),g=f?.src,_=B.useRef(null),v=B.useRef(null),y=B.useCallback(e=>{_.current=e,typeof t==`function`?t(e):t&&(t.current=e)},[t]),[b,x]=B.useState(!1),{margin:S,cells:C,numCells:w,calculatedImageSettings:T}=VT({value:r,level:a,minVersion:l,boostLevel:u,includeMargin:c,marginSize:d,imageSettings:f,size:i});B.useEffect(()=>{if(_.current!=null){let e=_.current,t=e.getContext(`2d`);if(!t)return;let n=C,r=v.current,a=T!=null&&r!==null&&r.complete&&r.naturalHeight!==0&&r.naturalWidth!==0;a&&T.excavation!=null&&(n=RT(C,T.excavation));let c=window.devicePixelRatio||1;e.height=e.width=i*c;let l=i/w*c;t.scale(l,l),t.fillStyle=o,t.fillRect(0,0,w,w),t.fillStyle=s,HT?t.fill(new Path2D(LT(n,S))):C.forEach(function(e,n){e.forEach(function(e,r){e&&t.fillRect(r+S,n+S,1,1)})}),T&&(t.globalAlpha=T.opacity),a&&t.drawImage(r,T.x+S,T.y+S,T.w,T.h)}}),B.useEffect(()=>{x(!1)},[g]);let E=CT({height:i,width:i},m),D=null;return g!=null&&(D=B.createElement(`img`,{src:g,key:g,style:{display:`none`},onLoad:()=>{x(!0)},ref:v,crossOrigin:T?.crossOrigin})),B.createElement(B.Fragment,null,B.createElement(`canvas`,CT({style:E,height:i,width:i,ref:y,role:`img`},h)),D)});UT.displayName=`QRCodeCanvas`;var WT=B.forwardRef(function(e,t){let n=e,{value:r,size:i=OT,level:a=kT,bgColor:o=AT,fgColor:s=jT,includeMargin:c=MT,minVersion:l=NT,boostLevel:u,title:d,marginSize:f,imageSettings:p}=n,m=wT(n,[`value`,`size`,`level`,`bgColor`,`fgColor`,`includeMargin`,`minVersion`,`boostLevel`,`title`,`marginSize`,`imageSettings`]),{margin:h,cells:g,numCells:_,calculatedImageSettings:v}=VT({value:r,level:a,minVersion:l,boostLevel:u,includeMargin:c,marginSize:f,imageSettings:p,size:i}),y=g,b=null;p!=null&&v!=null&&(v.excavation!=null&&(y=RT(g,v.excavation)),b=B.createElement(`image`,{href:p.src,height:v.h,width:v.w,x:v.x+h,y:v.y+h,preserveAspectRatio:`none`,opacity:v.opacity,crossOrigin:v.crossOrigin}));let x=LT(y,h);return B.createElement(`svg`,CT({height:i,width:i,viewBox:`0 0 ${_} ${_}`,ref:t,role:`img`},m),!!d&&B.createElement(`title`,null,d),B.createElement(`path`,{fill:o,d:`M0,0 h${_}v${_}H0z`,shapeRendering:`crispEdges`}),B.createElement(`path`,{fill:s,d:x,shapeRendering:`crispEdges`}),b)});WT.displayName=`QRCodeSVG`;function GT({data:e,onSubmit:t,submitted:n}){let r=e,i=(0,B.useRef)(!1),a=r?.value?.trim()??``;return(0,B.useEffect)(()=>{a&&!n&&!i.current&&(i.current=!0,t(``))},[a,n,t]),a?(0,V.jsxs)(`div`,{className:`qr-code`,children:[(0,V.jsx)(`div`,{className:`qr-code__canvas`,children:(0,V.jsx)(WT,{value:a,size:240,level:`M`,bgColor:`#ffffff`,fgColor:`#000000`})}),r?.label&&(0,V.jsx)(`div`,{className:`qr-code__label`,children:r.label})]}):(0,V.jsx)(`div`,{className:`qr-code qr-code--error`,children:`No QR data provided. Try again or ask the agent to generate a new QR code.`})}function KT(e){return e===0?`0 B`:e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function qT(e){return e.startsWith(`image/`)?(0,V.jsx)(ze,{size:18}):e===`application/pdf`?(0,V.jsx)(c,{size:18}):e===`text/calendar`?(0,V.jsx)(Te,{size:18}):e.startsWith(`text/`)?(0,V.jsx)(E,{size:18}):e===`application/zip`||e===`application/x-zip-compressed`?(0,V.jsx)(Ie,{size:18}):(0,V.jsx)(le,{size:18})}function JT({data:e,onSubmit:t,submitted:n,sessionKey:r}){let i=e,a=(0,B.useRef)(!1),o=!!(i?.attachmentId&&i?.filename);(0,B.useEffect)(()=>{o&&!n&&!a.current&&(a.current=!0,t(JSON.stringify({_lifecycle:!0,component:`file-attachment`,event:`rendered`,attachmentId:i.attachmentId,filename:i.filename})))},[o,n,t,i]);let s=(0,B.useCallback)(()=>{if(!i?.attachmentId||!r)return;let e=`/api/admin/attachment/${i.attachmentId}?session_key=${encodeURIComponent(r)}`,t=document.createElement(`a`);t.href=e,t.download=i.filename,t.click()},[i,r]);return o?(0,V.jsx)(O,{submitted:!1,submittedLabel:`Downloaded`,actions:[{label:`Download`,variant:`primary`,onClick:s,disabled:!r}],children:(0,V.jsxs)(`div`,{className:`file-attachment`,children:[(0,V.jsx)(`div`,{className:`file-attachment__icon`,children:qT(i.mimeType)}),(0,V.jsxs)(`div`,{className:`file-attachment__info`,children:[(0,V.jsx)(`div`,{className:`file-attachment__name`,title:i.filename,children:i.filename}),(0,V.jsxs)(`div`,{className:`file-attachment__meta`,children:[KT(i.sizeBytes),` · `,i.mimeType]})]}),(0,V.jsx)(`button`,{className:`file-attachment__download`,onClick:s,disabled:!r,title:`Download file`,children:(0,V.jsx)(L,{size:16})})]})}):null}var YT=/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/,XT=/^[a-z0-9-]$/;function ZT(e){return e?YT.test(e):!1}function QT(e){let t=e.toLowerCase(),n=``;for(let e of t)XT.test(e)&&(n+=e);for(;n.startsWith(`-`);)n=n.slice(1);return n}function $T(e){return[{key:`length`,label:`At least 8 characters`,met:e.length>=8},{key:`number`,label:`Contains a number`,met:/\d/.test(e)},{key:`special`,label:`Contains a special character`,met:/[^A-Za-z0-9]/.test(e)},{key:`whitespace`,label:`No spaces`,met:e.length>0&&!/\s/.test(e)}]}var eE=/https:\/\/dash\.cloudflare\.com\/argotunnel\?[^\s]+/,tE=/^OAUTH_URL:\s*(\S+)/,nE=/result=error\s+reason=([a-z0-9-]+)/;function rE(e){let t=e.match(tE);if(t)return t[1];let n=e.match(eE);return n?n[0]:null}function iE(e){let t=e.match(nE);return t?t[1]:null}var aE=/\[(\d+)\/(\d+)\]/,oE=2e3,sE=9e4;function cE(e){if(!e)return null;let t=e.match(aE);return t?parseInt(t[1],10):null}function lE({actionId:e,sessionKey:t,onExit:n,maxLines:r=1e3,selfRestart:i}){let[a,o]=(0,B.useState)([]),[s,c]=(0,B.useState)(null),[u,d]=(0,B.useState)(null),[f,p]=(0,B.useState)(null),[m,h]=(0,B.useState)(null),[g,_]=(0,B.useState)(!1),[v,y]=(0,B.useState)(null),b=(0,B.useRef)(null),x=(0,B.useRef)(null),S=(0,B.useRef)(0),C=(0,B.useRef)(null),w=(0,B.useRef)(null),T=(0,B.useRef)(null),E=(0,B.useRef)(null),D=(0,B.useRef)(null),O=(0,B.useRef)(null),k=(0,B.useRef)(n);k.current=n;let A=(0,B.useCallback)(()=>{D.current&&=(clearInterval(D.current),null),O.current&&=(clearTimeout(O.current),null)},[]),j=(0,B.useCallback)(e=>{e&&(b.current=e,y(e))},[]),M=(0,B.useCallback)(()=>{let n=S.current>0?`&from=${S.current}`:``,a=new EventSource(`/api/admin/actions/${encodeURIComponent(e)}/stream?session_key=${encodeURIComponent(t)}${n}`);C.current=a,a.addEventListener(`line`,e=>{try{let t=JSON.parse(e.data);if(S.current=t.byteOffset,o(e=>{let n=[...e,t];return n.length>r&&n.splice(0,n.length-r),n}),!m){let e=rE(t.text);e&&h(e)}let n=t.text.match(aE);n&&j(n[0]);let i=iE(t.text);i&&(x.current=i),p(null)}catch(e){console.error(`[ActionLogPanel] line parse failed:`,e)}}),a.addEventListener(`heartbeat`,e=>{try{let t=JSON.parse(e.data);T.current=t,c(t),j(t.last_phase),p(null)}catch(e){console.error(`[ActionLogPanel] heartbeat parse failed:`,e)}}),a.addEventListener(`exit`,e=>{try{let t=JSON.parse(e.data);d(t),k.current?.(t,x.current)}catch(e){console.error(`[ActionLogPanel] exit parse failed:`,e)}finally{a.close(),C.current=null,A(),_(!1)}}),a.onerror=()=>{if(u)return;a.close(),C.current=null;let e=cE(b.current);if(i&&e!==null&&e>=i.atPhase&&E.current!==null){console.log(`[ActionLogPanel] SSE drop during self-restart window (phase=${b.current}) — banner suppressed`),p(null),_(!0),N();return}p(`Connection lost — reconnecting…`),setTimeout(()=>{u||M()},1500)}},[e,t,r,i?.atPhase,A]);function N(){A(),D.current=setInterval(async()=>{try{let e=await fetch(`/api/admin/version`,{cache:`no-store`});if(!e.ok)return;let t=(await e.json())?.installed,n=E.current;t&&n&&t!==n&&(console.log(`[ActionLogPanel] admin-server version changed ${n}→${t} — reconnecting stream`),A(),_(!1),E.current=t,M())}catch{}},oE),O.current=setTimeout(()=>{console.log(`[ActionLogPanel] self-restart window timeout (${sE}ms) — reverting to error banner`),A(),_(!1),p(`New version did not come up — reconnecting…`),setTimeout(()=>{u||M()},1500)},sE)}(0,B.useEffect)(()=>(M(),()=>{C.current?.close(),C.current=null,A()}),[M,A]),(0,B.useEffect)(()=>{if(!i)return;let e=!1;return(async()=>{try{let t=await fetch(`/api/admin/version`,{cache:`no-store`});if(!t.ok)return;let n=await t.json();!e&&typeof n?.installed==`string`&&(E.current=n.installed)}catch{}})(),()=>{e=!0}},[e,i?.atPhase]),(0,B.useEffect)(()=>{let e=w.current;e&&e.scrollHeight-e.scrollTop-e.clientHeight<80&&(e.scrollTop=e.scrollHeight)},[a]);let P=e=>new Date(e).toLocaleTimeString(void 0,{hour12:!1}),ee=s?Math.floor(s.elapsed_since_last_line_ms/1e3):0,te=!u&&(f!==null||g),ne=te?`#666`:void 0,F=!u&&!te&&s&&ee>=5;return(0,V.jsxs)(`div`,{className:`action-log-panel`,style:{display:`flex`,flexDirection:`column`,height:`100%`,background:`#0a0a0a`,color:`#e8e8e8`,borderRadius:4,overflow:`hidden`,fontFamily:`ui-monospace, monospace`,fontSize:12},children:[(0,V.jsxs)(`div`,{style:{padding:`6px 10px`,borderBottom:`1px solid #222`,display:`flex`,gap:12,alignItems:`center`,flexWrap:`wrap`,fontSize:11},children:[(0,V.jsx)(`span`,{style:{color:`#888`},children:`action:`}),(0,V.jsx)(`span`,{children:e}),s&&(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{style:{color:`#888`,marginLeft:`auto`},children:`state:`}),(0,V.jsx)(`span`,{style:{color:ne},children:s.systemd_state}),v&&(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{style:{color:`#888`},children:`phase:`}),(0,V.jsx)(`span`,{style:{color:ne},children:v})]}),te&&(0,V.jsx)(`span`,{style:{color:`#888`,fontStyle:`italic`},children:`(stale)`}),F&&(0,V.jsxs)(`span`,{style:{color:`#e4a657`,display:`inline-flex`,alignItems:`center`,gap:4},children:[(0,V.jsx)(l,{size:11}),` silent `,ee,`s`]})]})]}),m&&!u&&(0,V.jsxs)(`div`,{style:{padding:`8px 10px`,borderBottom:`1px solid #222`,background:`#1b1f2b`,display:`flex`,alignItems:`center`,gap:8},children:[(0,V.jsx)(`span`,{style:{color:`#cbd5f5`},children:`Cloudflare needs you to authorise this device.`}),(0,V.jsxs)(`a`,{href:m,target:`_blank`,rel:`noopener noreferrer`,style:{marginLeft:`auto`,color:`#fff`,background:`#f6821f`,padding:`4px 10px`,borderRadius:3,textDecoration:`none`,display:`inline-flex`,alignItems:`center`,gap:6},children:[`Authorise in Cloudflare `,(0,V.jsx)(Fe,{size:12})]})]}),f&&!g&&(0,V.jsxs)(`div`,{style:{padding:`6px 10px`,background:`#3a2b10`,color:`#ffc987`,display:`flex`,alignItems:`center`,gap:8},children:[(0,V.jsx)(ge,{size:12,className:`spin`}),` `,f]}),g&&(0,V.jsxs)(`div`,{style:{padding:`6px 10px`,background:`#132319`,color:`#9aeab0`,display:`flex`,alignItems:`center`,gap:8},children:[(0,V.jsx)(ge,{size:12,className:`spin`}),` Waiting for new version…`]}),(0,V.jsxs)(`div`,{ref:w,style:{flex:1,overflow:`auto`,padding:`6px 10px`,lineHeight:1.45,whiteSpace:`pre-wrap`,wordBreak:`break-word`},children:[a.length===0&&!u&&(0,V.jsx)(`div`,{style:{color:`#888`},children:`Waiting for output…`}),a.map(e=>(0,V.jsxs)(`div`,{style:{display:`flex`,gap:10,color:e.stream===`stderr`?`#ff9999`:`#e8e8e8`},children:[(0,V.jsx)(`span`,{style:{color:`#666`,flexShrink:0},children:P(e.ts)}),(0,V.jsx)(`span`,{children:e.text})]},e.byteOffset))]}),u&&(0,V.jsxs)(`div`,{style:{padding:`8px 10px`,borderTop:`1px solid #222`,background:u.code===0?`#14331b`:`#3a1717`,color:u.code===0?`#9aeab0`:`#ff9999`,display:`flex`,alignItems:`center`,gap:8},children:[u.code===0?(0,V.jsx)(De,{size:13}):(0,V.jsx)(l,{size:13}),(0,V.jsxs)(`span`,{children:[u.code===0?`Completed`:`Failed (exit ${u.code})`,` · `,Math.round(u.duration_ms/1e3),`s`]})]})]})}var uE={title:`Authorise the tunnel in your own browser`,intro:`The Pi's browser couldn't drive the Cloudflare consent page automatically. Open the dashboard in your own browser and follow these steps:`,steps:[`The "Authorize Cloudflare Tunnel" page shows every zone on the currently-active account.`,`Click the zone you want the tunnel to be able to route DNS for.`,`Click Authorize. The browser closes or shows "You can close this page."`,`Return here and click "Try again" — the cert will land and setup will continue.`],footnote:`If the account shown in the top-left of the dashboard is not the one that owns your zone, click the account-name dropdown first and switch accounts before authorising.`},dE=18e4,fE=4096;function pE(e){let t=new TextEncoder().encode(e);return t.length<=fE?e:`${new TextDecoder(`utf-8`,{fatal:!1}).decode(t.slice(0,fE))}\n… ${t.length-fE} bytes truncated (total ${t.length} bytes)`}function mE({data:e,onSubmit:t,submitted:n,sessionKey:r}){let i=e,[a,o]=(0,B.useState)({status:`loading`}),[s,c]=(0,B.useState)(``),[l,u]=(0,B.useState)(``),[d,f]=(0,B.useState)(``),[p,m]=(0,B.useState)(``),[h,g]=(0,B.useState)(``),[_,v]=(0,B.useState)(``),[y,b]=(0,B.useState)(!1),[x,S]=(0,B.useState)(!1),[C,w]=(0,B.useState)(!1),[T,E]=(0,B.useState)(null),[D,k]=(0,B.useState)(null),[A,j]=(0,B.useState)(null),M=(0,B.useRef)(null),N=(0,B.useRef)(new Set),P=(0,B.useCallback)(async()=>{if(!r){o({status:`error`,field:`session`,message:`No session key available — refresh the page.`});return}o({status:`loading`});try{let e=await(await fetch(`/api/admin/cloudflare/domains?session_key=${encodeURIComponent(r)}`,{method:`GET`,headers:{Accept:`application/json`}})).json();if(!e.ok){o({status:`error`,field:e.field,message:e.message,output:e.output,correlationId:e.correlationId,streamLogPath:e.streamLogPath});return}if(e.domains.length===0){o({status:`empty`});return}o({status:`populated`,domains:e.domains}),u(t=>t||e.domains[0]),m(t=>t||e.domains[0])}catch(e){o({status:`error`,field:`script`,message:e instanceof Error?e.message:`Network error loading domains.`})}},[r]);(0,B.useEffect)(()=>{P()},[P]),(0,B.useEffect)(()=>()=>{M.current&&clearTimeout(M.current)},[]);let ee=a.status===`populated`?a.domains:[],te=e=>{n||x||(c(QT(e)),j(null))},ne=e=>{n||x||(f(QT(e)),j(null))},F=ZT(s)&&l.length>0,I=d!==``,re=!I||ZT(d)&&p.length>0,ie=F?`${s.replace(/-+$/,``)}.${l}`:``,ae=I&&re?`${d.replace(/-+$/,``)}.${p}`:``,oe=!(ae&&ae===ie),se=$T(_),L=se.every(e=>e.met),ce=a.status===`populated`&&F&&re&&oe&&L&&!x,le=s!==``&&!ZT(s)?`Use lowercase letters, numbers, and hyphens. Start and end with a letter or number.`:null,ue=I&&!ZT(d)?`Use lowercase letters, numbers, and hyphens. Start and end with a letter or number.`:oe?null:`Public address must differ from the admin address.`,de=A?.field===`admin`?A.message:null,fe=A?.field===`public`?A.message:null,pe=A?.field===`password`?A.message:null,me=A?.field===`script`||A?.field===`request`?A.message:null,he=()=>{w(!1),j(null),S(!1)},ge=async()=>{if(!ce)return;if(!r){j({field:`request`,message:`No session key available — refresh the page.`});return}S(!0),j(null),w(!1);let e=new AbortController;M.current=setTimeout(()=>{e.abort(),w(!0)},dE);let n={session_key:r,adminLabel:s.replace(/-+$/,``),adminDomain:l,password:_};I&&(n.publicLabel=d.replace(/-+$/,``),n.publicDomain=p),h&&(n.apex=h);try{let r=await fetch(`/api/admin/cloudflare/setup`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(n),signal:e.signal});M.current&&clearTimeout(M.current),M.current=null;let i=await r.json();if(!i.ok){j({field:i.field,message:i.message,output:i.output,correlationId:i.correlationId,streamLogPath:i.streamLogPath});let e=i.streamLogPath?`\n\nstream log: ${i.streamLogPath}`:``;t(i.output&&i.output.length>0?`Cloudflare setup failed: ${i.message}\n\n${i.output}${e}`:`Cloudflare setup failed: ${i.message}${e}`),S(!1);return}if(i.actionId){E(i.actionId);return}t(i.output)}catch(t){if(M.current&&clearTimeout(M.current),M.current=null,e.signal.aborted)return;j({field:`script`,message:t instanceof Error?t.message:`Network error — try again.`}),S(!1)}},_e=(0,B.useMemo)(()=>s?`${s}.${l||`your-domain`}`:`your-name.${l||`your-domain`}`,[s,l]);if(a.status===`loading`)return(0,V.jsx)(O,{submitted:!1,actions:[],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[i?.title&&(0,V.jsx)(`div`,{className:`form-input__title`,children:i.title}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`Reading the domains on your Cloudflare account…`})]})});if(a.status===`empty`)return(0,V.jsx)(O,{submitted:!1,actions:[{label:`Retry`,variant:`primary`,onClick:P}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:`No domains on this Cloudflare account`}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`Add a domain in the Cloudflare dashboard first, then click Retry.`}),(0,V.jsxs)(`ol`,{className:`form-input__field-desc`,style:{margin:0,paddingLeft:20},children:[(0,V.jsxs)(`li`,{children:[`With the correct account active (top-left), click `,(0,V.jsx)(`strong`,{children:`Websites`}),` in the left-hand sidebar.`]}),(0,V.jsxs)(`li`,{children:[`Click the `,(0,V.jsx)(`strong`,{children:`Add a site`}),` button.`]}),(0,V.jsxs)(`li`,{children:[`Enter the bare domain (e.g. `,(0,V.jsx)(`code`,{children:`example.com`}),`) and click `,(0,V.jsx)(`strong`,{children:`Continue`}),`.`]}),(0,V.jsxs)(`li`,{children:[`Review Cloudflare's imported DNS records — preserve website (`,(0,V.jsx)(`code`,{children:`A`}),`), email (`,(0,V.jsx)(`code`,{children:`MX`}),`), and verification (`,(0,V.jsx)(`code`,{children:`TXT`}),`) entries.`]}),(0,V.jsx)(`li`,{children:`Copy Cloudflare's two nameservers and set them on your domain registrar.`}),(0,V.jsxs)(`li`,{children:[`Return to Cloudflare and click `,(0,V.jsx)(`strong`,{children:`Check nameservers`}),`. Wait for the zone to show `,(0,V.jsx)(`strong`,{children:`Active`}),`.`]})]})]})});if(a.status===`error`){let e=a.field===`dashboard`,t=e?`Not signed into Cloudflare on this device`:`Could not read domains from Cloudflare`,n=e?`Sign in again at the Cloudflare dashboard in the VNC browser, then click Retry. If the VNC browser is not open, ask me to run vnc.sh restart.`:a.message;return(0,V.jsx)(O,{submitted:!1,actions:[{label:`Retry`,variant:`primary`,onClick:P}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:t}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:n}),a.output&&a.output.length>0&&(0,V.jsx)(`pre`,{style:{marginTop:8,padding:8,maxHeight:280,overflow:`auto`,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:12,lineHeight:1.4,whiteSpace:`pre-wrap`,wordBreak:`break-word`,background:`var(--surface-2, rgba(0,0,0,0.04))`,borderRadius:4},children:pE(a.output)}),(a.correlationId||a.streamLogPath)&&(0,V.jsxs)(`div`,{style:{marginTop:8,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:11,color:`var(--text-secondary)`,wordBreak:`break-all`},children:[a.correlationId&&(0,V.jsxs)(`div`,{children:[`conversationId: `,a.correlationId]}),a.streamLogPath&&(0,V.jsxs)(`div`,{children:[`stream log: `,a.streamLogPath]})]})]})})}return D?(0,V.jsx)(O,{submitted:!1,actions:[{label:`Try again`,variant:`primary`,onClick:()=>{k(null),E(null),j(null),S(!1),w(!1)}}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:D.card.title}),(0,V.jsx)(`div`,{className:`form-input__desc`,style:{whiteSpace:`pre-wrap`},children:D.card.intro}),(0,V.jsx)(`ol`,{style:{marginTop:10,paddingLeft:20,lineHeight:1.6},children:D.card.steps.map((e,t)=>(0,V.jsx)(`li`,{children:e},t))}),D.card.footnote&&(0,V.jsx)(`div`,{style:{marginTop:10,fontStyle:`italic`,color:`#666`},children:D.card.footnote})]})}):T&&r?(0,V.jsx)(O,{submitted:!1,actions:[],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[i?.title&&(0,V.jsx)(`div`,{className:`form-input__title`,children:i.title}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`Cloudflare setup in progress — keep this panel open until it completes.`}),(0,V.jsx)(`div`,{style:{marginTop:10,height:360},children:(0,V.jsx)(lE,{actionId:T,sessionKey:r,onExit:(e,n)=>{if(N.current.has(T)){console.log(`[CloudflareSetupForm] suppressed duplicate completion dispatch actionId=${T}`);return}if(N.current.add(T),e.code===0){t(`Cloudflare setup completed (actionId: ${T}).`);return}if(n===`authorize-button-not-found`){k({card:uE,actionId:T,exitCode:e.code});return}t(`Cloudflare setup failed with exit code ${e.code} (actionId: ${T}).`)}})})]})}):C?(0,V.jsx)(O,{submitted:!1,actions:[{label:`Reset form`,variant:`ghost`,onClick:he}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:`Setup is taking longer than expected`}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`The request is still running on the device — check the chat for live progress. Do not resubmit. If the script fails, the error will appear in chat and you can retry from there.`})]})}):(0,V.jsx)(O,{submitted:n,submittedLabel:`Submitting…`,actions:[{label:x?`Setting up…`:i?.submitLabel??`Set up Cloudflare`,variant:`primary`,disabled:!ce,onClick:ge}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[i?.title&&(0,V.jsx)(`div`,{className:`form-input__title`,children:i.title}),i?.description&&(0,V.jsx)(`div`,{className:`form-input__desc`,children:i.description}),(0,V.jsxs)(`div`,{className:`form-input__fields`,children:[(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-admin-label`,children:[`Admin address`,(0,V.jsx)(`span`,{className:`form-input__required`,children:`*`})]}),(0,V.jsxs)(`div`,{className:`tunnel-route__row`,children:[(0,V.jsx)(`input`,{id:`cf-admin-label`,className:`form-input__input tunnel-route__input`,type:`text`,autoCapitalize:`none`,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,placeholder:`your-name`,value:s,onChange:e=>te(e.target.value),disabled:n||x}),(0,V.jsx)(hE,{id:`cf-admin-domain`,value:l,options:ee,onChange:u,disabled:n||x})]}),(0,V.jsx)(`div`,{className:`form-input__field-desc`,children:`Where you access your platform remotely, via your main domain.`}),(0,V.jsxs)(`div`,{className:`form-input__field-desc`,children:[`Preview: `,(0,V.jsx)(`span`,{className:`tunnel-route__preview`,children:_e})]}),(de||le)&&(0,V.jsx)(`div`,{className:`tunnel-route__error`,children:de??le})]}),(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-public-label`,children:[`Public address`,(0,V.jsx)(`span`,{className:`tunnel-route__optional`,children:`Optional`})]}),(0,V.jsxs)(`div`,{className:`tunnel-route__row`,children:[(0,V.jsx)(`input`,{id:`cf-public-label`,className:`form-input__input tunnel-route__input`,type:`text`,autoCapitalize:`none`,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,placeholder:`leave empty to skip`,value:d,onChange:e=>ne(e.target.value),disabled:n||x}),(0,V.jsx)(hE,{id:`cf-public-domain`,value:p,options:ee,onChange:m,disabled:n||x||!I})]}),(0,V.jsx)(`div`,{className:`form-input__field-desc`,children:`Where visitors engage with your public agents, via your main domain. Leave empty for admin-only access.`}),(fe||ue)&&(0,V.jsx)(`div`,{className:`tunnel-route__error`,children:fe??ue})]}),(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-apex`,children:[`Proxy apex`,(0,V.jsx)(`span`,{className:`tunnel-route__optional`,children:`Optional`})]}),(0,V.jsxs)(`select`,{id:`cf-apex`,className:`form-input__input`,value:h,onChange:e=>g(e.target.value),disabled:n||x,children:[(0,V.jsx)(`option`,{value:``,children:`None`}),ee.map(e=>(0,V.jsx)(`option`,{value:e,children:e},e))]}),(0,V.jsx)(`div`,{className:`form-input__field-desc`,children:`Alternative domain for public agent visitors.`})]}),(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-password`,children:[`Admin password`,(0,V.jsx)(`span`,{className:`form-input__required`,children:`*`})]}),(0,V.jsxs)(`div`,{className:`tunnel-route__row`,children:[(0,V.jsx)(`input`,{id:`cf-password`,className:`form-input__input tunnel-route__input`,type:y?`text`:`password`,autoCapitalize:`none`,autoComplete:`new-password`,autoCorrect:`off`,spellCheck:!1,value:_,onChange:e=>{v(e.target.value),j(null)},disabled:n||x}),(0,V.jsx)(`button`,{type:`button`,className:`tunnel-route__suffix`,onClick:()=>b(e=>!e),disabled:n||x,style:{cursor:`pointer`,border:`none`,background:`inherit`},"aria-label":y?`Hide password`:`Show password`,children:y?`Hide`:`Show`})]}),(0,V.jsx)(`ul`,{className:`form-input__field-desc`,style:{margin:0,paddingLeft:16},children:se.map(e=>(0,V.jsxs)(`li`,{style:{color:e.met?`var(--text)`:`var(--text-secondary)`},children:[e.met?`✓`:`○`,` `,e.label]},e.key))}),pe&&(0,V.jsx)(`div`,{className:`tunnel-route__error`,children:pe})]}),me&&(0,V.jsxs)(`div`,{className:`tunnel-route__error`,role:`alert`,children:[(0,V.jsx)(`div`,{children:me}),A?.output&&A.output.length>0&&(0,V.jsx)(`pre`,{style:{marginTop:8,padding:8,maxHeight:280,overflow:`auto`,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:12,lineHeight:1.4,whiteSpace:`pre-wrap`,wordBreak:`break-word`,background:`var(--surface-2, rgba(0,0,0,0.04))`,borderRadius:4},children:pE(A.output)}),(A?.correlationId||A?.streamLogPath)&&(0,V.jsxs)(`div`,{style:{marginTop:8,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:11,color:`var(--text-secondary)`,wordBreak:`break-all`},children:[A.correlationId&&(0,V.jsxs)(`div`,{children:[`conversationId: `,A.correlationId]}),A.streamLogPath&&(0,V.jsxs)(`div`,{children:[`stream log: `,A.streamLogPath]})]})]})]})]})})}function hE({id:e,value:t,options:n,onChange:r,disabled:i}){return(0,V.jsx)(`select`,{id:e,className:`tunnel-route__suffix`,value:t,onChange:e=>r(e.target.value),disabled:i,style:{cursor:i?`default`:`pointer`,border:`none`},children:n.map(e=>(0,V.jsxs)(`option`,{value:e,children:[`.`,e]},e))})}var gE={"single-select":b,"multi-select":P,confirm:Gt,"info-card":Kt,"action-list":qt,form:Jt,progress:Xt,"browser-viewer":Qt,"rich-content-editor":uT,"grid-editor":_T,"qr-code":GT,"file-attachment":JT,"action-buttons":j,"cloudflare-setup-form":mE,"output-style":b,"thinking-view":b,"plugin-selector":P};function _E(e){let{data:t,filePath:n,onOpen:r}=e,i=n.split(`/`).pop()||n||`document.md`,a=(i.split(`.`).pop()||`doc`).toUpperCase().slice(0,6),o=n||i,s=(0,B.useRef)(!1);return(0,B.useEffect)(()=>{s.current||(s.current=!0,r({docId:o,name:i,content:t.content??``}))},[o,i,t.content,r]),(0,V.jsx)(`div`,{className:`component-card artefact-ref-card`,children:(0,V.jsxs)(`button`,{type:`button`,className:`artefact-ref-button`,onClick:()=>r({docId:o,name:i,content:t.content??``}),children:[(0,V.jsx)(`span`,{className:`artefact-ref-icon`,children:(0,V.jsx)(c,{size:16})}),(0,V.jsxs)(`span`,{className:`artefact-ref-meta`,children:[(0,V.jsx)(`span`,{className:`artefact-ref-name`,children:i}),(0,V.jsxs)(`span`,{className:`artefact-ref-tag`,children:[a,` · open in artefact pane`]})]}),(0,V.jsx)(`span`,{className:`artefact-ref-open`,children:(0,V.jsx)(Fe,{size:14})})]})})}function vE({name:e,data:t,onSubmit:n,submitted:r,isStreaming:i,sessionKey:a,onOpenArtefact:o}){if(e===`document-editor`&&o){let e=t??{};return(0,V.jsx)(_E,{data:e,filePath:e.filePath||e.title||`document.md`,onOpen:o})}if(e===`document-editor`)return(0,V.jsx)(Jw,{data:t,onSubmit:n,submitted:r,isStreaming:i});let s=gE[e];return s?(0,V.jsx)(s,{data:t,onSubmit:n,submitted:r,isStreaming:i,sessionKey:a}):(console.warn(`[ComponentRenderer] Unknown component: "${e}". Registered: ${Object.keys(gE).join(`, `)}`),(0,V.jsx)(`div`,{className:`component-card component-card--error`,children:(0,V.jsxs)(`p`,{style:{fontFamily:`var(--font-body)`,fontSize:12,color:`var(--text-secondary)`},children:[`Component “`,e,`” is not available. This may require a platform update.`]})}))}var yE=/\b(?:fail|error|unable|could not|cannot|CAPTCHA|SIGN_IN_REQUIRED)\b/i;function bE(e){let t=new Date(e),n=new Date,r=String(t.getHours()).padStart(2,`0`),i=String(t.getMinutes()).padStart(2,`0`);return t.toDateString()===n.toDateString()?`${r}:${i}`:`${String(t.getDate()).padStart(2,`0`)}-${String(t.getMonth()+1).padStart(2,`0`)}-${t.getFullYear()} ${r}:${i}`}var xE=[pt,ut,be,ct,Ne];function SE({size:e=13}){let[t,n]=(0,B.useState)(0);(0,B.useEffect)(()=>{let e=setInterval(()=>n(e=>(e+1)%8),300);return()=>clearInterval(e)},[]);let r=t<=4?t:8-t,i=xE[r];return(0,V.jsx)(i,{size:e,className:`star-loader star-frame-${r}`})}var CE=[`Thinking`,`Reasoning`,`Analyzing`,`Considering`,`Reflecting`,`Evaluating`,`Weighing`,`Synthesizing`,`Processing`,`Exploring`,`Examining`,`Assessing`,`Reviewing`,`Contemplating`,`Deliberating`],wE=60,TE=1500;function EE(){let e=(0,B.useRef)(-1),t=(0,B.useRef)(void 0),[n,r]=(0,B.useState)(()=>{let t=Math.floor(Math.random()*CE.length);return e.current=t,CE[t]+`…`});return(0,B.useEffect)(()=>{let n=!1;function i(){let t;do t=Math.floor(Math.random()*CE.length);while(t===e.current&&CE.length>1);return e.current=t,t}function a(){if(n)return;let e=CE[i()]+`…`,o=0;function s(){n||(o++,r(e.slice(0,o)),o<e.length?t.current=setTimeout(s,wE):t.current=setTimeout(a,TE))}t.current=setTimeout(s,wE)}return t.current=setTimeout(a,TE),()=>{n=!0,clearTimeout(t.current)}},[]),(0,V.jsx)(`span`,{className:`tl-thinking-typewriter`,children:n})}function DE(e,t){let n=e=>String(e??``);switch(e){case`Read`:return`Read ${n(t.file_path??t.path)}`;case`Write`:return`Write ${n(t.file_path??t.path)}`;case`Edit`:return`Edit ${n(t.file_path??t.path)}`;case`Bash`:return n(t.description??String(t.command??``).slice(0,70));case`Glob`:return`Glob ${n(t.pattern)}`;case`Grep`:return`Grep "${n(t.pattern)}" in ${n(t.path??`.`)}`;case`WebFetch`:return`Fetch ${n(t.url)}`;case`Agent`:return n(t.description??t.task??`Subagent`);case`Skill`:return n(t.skill??`Skill`);default:{let r=e.match(/^mcp__[^_]+__(.+)$/),i=r?r[1]:e,a=Object.keys(t)[0];return a?`${i}: ${n(t[a]).slice(0,50)}`:i}}}function OE({name:e,size:t=12}){let n=e.match(/^mcp__[^_]+__(.+)$/);switch(n?n[1]:e){case`Read`:return(0,V.jsx)(c,{size:t});case`Write`:return(0,V.jsx)(Ve,{size:t});case`Edit`:return(0,V.jsx)(Be,{size:t});case`Bash`:return(0,V.jsx)(mt,{size:t});case`Glob`:case`Grep`:return(0,V.jsx)(de,{size:t});case`WebFetch`:return(0,V.jsx)(D,{size:t});case`Agent`:return(0,V.jsx)(Ce,{size:t});case`Skill`:return(0,V.jsx)(ut,{size:t});case`memory-search`:return(0,V.jsx)(Me,{size:t});case`memory-write`:return(0,V.jsx)(je,{size:t});case`task-list`:return(0,V.jsx)(R,{size:t});case`task-create`:return(0,V.jsx)(ke,{size:t});case`contact-create`:return(0,V.jsx)(ht,{size:t});default:return(0,V.jsx)(_t,{size:t})}}function kE(e){let t=new Map,n=new Map;return e.forEach((e,r)=>{if(e.type===`tool_use`){let t=n.get(e.name)??[];t.push(r),n.set(e.name,t)}else if(e.type===`tool_result`){let i=n.get(e.name);i?.length&&t.set(i.shift(),r)}}),t}function AE(e){return e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function jE(e){let t=``;switch(e.type){case`text`:case`thinking`:t=e.content??``;break;case`tool_use`:t=JSON.stringify(e.input??{});break;case`tool_result`:t=e.output??``;break;case`subagent_start`:t=e.task??``;break;case`subagent_end`:t=e.result??``;break;case`component`:t=JSON.stringify(e.data??{});break;case`status`:t=e.message??``;break;default:return 0}return Math.ceil(t.length/4)}function ME(e){if(e<60)return`${e}s`;let t=Math.floor(e/60),n=e%60;return n>0?`${t}m ${n}s`:`${t}m`}function NE(e){if(e<60)return`${e.toFixed(1)}s`;let t=Math.floor(e/60),n=e%60;return n>=.1?`${t}m ${Math.floor(n)}s`:`${t}m`}function PE({icon:e,isPending:t,isError:n,doneClass:r=`tl-done`,summary:i,detail:a,tokens:o,elapsed:s,isLast:c,expanded:l,onToggle:u,selectCheck:d}){let f=!!a;return(0,V.jsxs)(`div`,{className:`tl-step`,children:[d,(0,V.jsxs)(`div`,{className:`tl-col`,children:[(0,V.jsx)(`div`,{className:`tl-icon${t?` tl-pending`:n?` tl-error`:` ${r}`}`,children:e}),!c&&(0,V.jsx)(`div`,{className:`tl-line tl-line-grow`})]}),(0,V.jsxs)(`div`,{className:`tl-body`,children:[(0,V.jsxs)(`div`,{className:`tl-row`,onClick:f?()=>{window.getSelection()?.toString()||u()}:void 0,style:{cursor:f?`pointer`:`default`},children:[(0,V.jsx)(`span`,{className:`tl-summary`,children:i}),o>0&&(0,V.jsxs)(`span`,{className:`tl-step-tokens`,children:[(0,V.jsx)(vt,{size:9}),AE(o)]}),(0,V.jsx)(`span`,{className:`tl-step-elapsed`,children:NE(s)}),f&&(0,V.jsx)(`span`,{className:`tl-chevron`,children:l?(0,V.jsx)(p,{size:10}):(0,V.jsx)(Ee,{size:10})})]}),f&&l&&(0,V.jsx)(`pre`,{className:`tl-detail`,children:a})]})]})}function FE({events:e,isStreaming:t,elapsedSeconds:n,streamStartMs:r,expandAll:i,onCompactNow:a,isCompacting:o,sessionCompacted:s,onComponentSubmit:c,submittedComponents:l,timestamp:u,selectionMode:d,selectedItems:f,onToggleItem:m,sessionKey:g,onOpenArtefact:_}){let[v,y]=(0,B.useState)(new Set),[b,S]=(0,B.useState)(!t),C=(0,B.useRef)(t);(0,B.useEffect)(()=>{C.current&&!t&&S(!0),!C.current&&t&&S(!1),C.current=t},[t]);let T=(0,B.useRef)(new Map),E=(0,B.useRef)(0),[D,O]=(0,B.useState)(Date.now());(0,B.useEffect)(()=>{let t=Date.now();for(let n=E.current;n<e.length;n++)T.current.has(n)||T.current.set(n,t);E.current=e.length},[e]),(0,B.useEffect)(()=>{if(!t)return;let e=setInterval(()=>O(Date.now()),100);return()=>clearInterval(e)},[t]);let k=e=>{y(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},A=(e,t)=>{let n=e===ae[0]?.i&&r?r:T.current.get(e)??D,i=t===void 0?D:T.current.get(t)??D;return Math.max(0,(i-n)/1e3)},j=kE(e),M=new Set(j.values()),N=e.map((e,t)=>({e,i:t})).filter(({e})=>e.type===`text`),P=new Set(t?[]:e.map((e,t)=>({e,i:t})).filter(({e})=>e.type===`component`).map(({i:e})=>e)),ee=e=>e.type===`component`&&[`browser-viewer`,`qr-code`].includes(e.name);function ne(e){if(!d||t||!u)return null;let n=`${u}_${e}`;return(0,V.jsx)(`div`,{className:`tl-select-check`,onClick:e=>e.stopPropagation(),children:(0,V.jsx)(_e,{checked:f?.has(n)??!1,onChange:()=>m?.(n)})})}let F=e.findLast(e=>e.type===`usage`),I=e.findLast(e=>e.type===`history_usage`),re=e.findLast(e=>e.type===`status`),ie=e.some(e=>e.type===`subagent_start`),ae=e.map((e,t)=>({e,i:t})).filter(({e,i:n})=>e.type!==`text`&&e.type!==`usage`&&e.type!==`rate_limit`&&e.type!==`history_usage`&&e.type!==`status`&&e.type!==`done`&&e.type!==`subagent_progress`&&!M.has(n)&&!P.has(n)&&!(t&&e.type===`component`&&!ee(e))&&!(e.type===`tool_use`&&e.name===`Agent`&&ie)),se=new Map;for(let e=0;e<ae.length-1;e++)se.set(ae[e].i,ae[e+1].i);let L=(()=>{let e=[],t=!1,n=0;for(let{e:r}of ae)if(n+=jE(r),r.type===`thinking`)t=!0;else if(r.type===`tool_use`){let t=r.name;e.includes(t)||e.push(t)}let r=[`${ae.length} step${ae.length===1?``:`s`}`];if(e.length>0){let t=e.slice(0,3),n=e.length-t.length;r.push(t.join(`, `)+(n>0?` +${n}`:``))}return{text:r.join(` · `),hasThinking:t,tokenSum:n}})(),ce=F?.peak_request_pct==null?F?.context_window?Math.round((F.input_tokens+F.cache_creation_tokens+F.cache_read_tokens)/F.context_window*100):null:Math.round(F.peak_request_pct*100),le=ae.length>0,ue=N.length>0,de=new Set(ae.map(e=>e.i)),fe=new Set(N.map(e=>e.i)),pe=[];for(let t=0;t<e.length;t++){let n=e[t],r=null;if(de.has(t)?r=`steps`:fe.has(t)&&(r=`text`),!r)continue;let i=pe[pe.length-1];i&&i.role===r?i.items.push({e:n,i:t}):pe.push({role:r,items:[{e:n,i:t}]})}let me=N.length>0?N[N.length-1].i:-1;function he(n,r,a,o){let s=o===a.length-1;if(n.type===`thinking`){let e=i===void 0?!v.has(r):i,t=se.get(r);return(0,V.jsxs)(`div`,{className:`tl-step`,children:[ne(r),(0,V.jsxs)(`div`,{className:`tl-col`,children:[(0,V.jsx)(`div`,{className:`tl-icon tl-dim`,children:(0,V.jsx)(qe,{size:11})}),!s&&(0,V.jsx)(`div`,{className:`tl-line tl-line-grow`})]}),(0,V.jsx)(`div`,{className:`tl-body`,children:(0,V.jsxs)(`div`,{className:`tl-row tl-row-top`,onClick:()=>{window.getSelection()?.toString()||k(r)},style:{cursor:`pointer`},children:[(0,V.jsx)(`div`,{className:`tl-thinking-col`,children:e?(0,V.jsx)(`div`,{className:`tl-thinking-body`,children:n.content}):(0,V.jsxs)(`span`,{className:`tl-summary tl-thinking-label`,children:[n.content.slice(0,80),n.content.length>80?`…`:``]})}),jE(n)>0&&(0,V.jsxs)(`span`,{className:`tl-step-tokens`,children:[(0,V.jsx)(vt,{size:9}),AE(jE(n))]}),(0,V.jsx)(`span`,{className:`tl-step-elapsed`,children:NE(A(r,t))}),(0,V.jsx)(`span`,{className:`tl-chevron`,children:e?(0,V.jsx)(p,{size:10}):(0,V.jsx)(Ee,{size:10})})]})})]},r)}if(n.type===`status`)return(0,V.jsx)(`div`,{className:`tl-status`,children:n.message},r);if(n.type===`tool_use`){let t=j.get(r),a=t===void 0?void 0:e[t],o=a===void 0,c=a?.type===`tool_result`&&!!a.error,l=o?(0,V.jsx)(Ze,{size:11,className:`tl-spinner`}):c?(0,V.jsx)(oe,{size:11}):(0,V.jsx)(OE,{name:n.name,size:11}),u=n,d=a?.type===`tool_result`?`Input:
343
+ `).filter(e=>e.trim());if(n.length<=1&&!n[0]?.includes(` `)&&!n[0]?.includes(`,`))return;e.preventDefault();let r=n.slice(0,dT).map(mT);n.length>dT&&console.warn(`Grid editor: paste truncated from ${n.length} to ${dT} rows`);let{row:i,col:a}=g;h(e=>{let t=e.map(e=>[...e]);for(let e=0;e<r.length;e++){let n=i+e;for(;n>=t.length;)t.push(Array(f.length).fill(``));for(let i=0;i<r[e].length;i++){let o=a+i;o<f.length&&(t[n][o]=r[e][i])}}return t}),A()},[g,f.length,A]),re=(0,B.useCallback)(()=>{let e=hT(f,m),t=new Blob([e],{type:`text/csv;charset=utf-8;`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`${d.replace(/[^a-zA-Z0-9]/g,`_`)}.csv`,r.click(),URL.revokeObjectURL(n)},[f,m,d]),ie=(0,B.useCallback)(()=>{$w({title:d,content:eT(f,m),brandName:l,brandLogo:u})},[d,f,m,l,u]),ae=(0,B.useCallback)((e,t)=>{t.preventDefault(),C.current={colIndex:e,startX:t.clientX,startWidth:x[e]};let n=null,r=e=>{C.current&&(n&&cancelAnimationFrame(n),n=requestAnimationFrame(()=>{if(!C.current)return;let t=e.clientX-C.current.startX,n=Math.max(fT,C.current.startWidth+t);S(e=>{let t=[...e];return t[C.current.colIndex]=n,t})}))},i=()=>{C.current=null,n&&cancelAnimationFrame(n),window.removeEventListener(`mousemove`,r),window.removeEventListener(`mouseup`,i)};window.addEventListener(`mousemove`,r),window.addEventListener(`mouseup`,i)},[x]),se=(0,B.useCallback)(()=>{g&&M.openCommentInput()},[g,M]),ce=(0,B.useCallback)(()=>{if(!g)return;let e=m[g.row]?.[g.col]??``;M.submitComment(e,{row:g.row,column:g.col})},[g,m,M]);if(n)return(0,V.jsxs)(`div`,{className:`editor-base grid-editor grid-editor--submitted`,children:[(0,V.jsxs)(`div`,{className:`editor-base__bar`,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),(0,V.jsx)(`div`,{className:`editor-base__actions`,children:(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Submitted`})})]}),(0,V.jsx)(`div`,{className:`grid-editor__body`,children:(0,V.jsx)(`div`,{className:`grid-editor__table-wrap`,children:(0,V.jsxs)(`table`,{className:`grid-editor__table`,role:`grid`,"aria-label":d,children:[(0,V.jsx)(`thead`,{children:(0,V.jsx)(`tr`,{children:f.map((e,t)=>(0,V.jsx)(`th`,{className:`grid-editor__col-header`,style:{width:x[t]},children:e},t))})}),(0,V.jsx)(`tbody`,{children:m.map((e,t)=>(0,V.jsx)(`tr`,{children:e.map((e,t)=>(0,V.jsx)(`td`,{className:`grid-editor__cell`,role:`gridcell`,style:{width:x[t]},children:(0,V.jsx)(`span`,{className:`grid-editor__cell-text`,children:e})},t))},t))})]})})})]});let le=typeof window<`u`&&window.innerWidth<640,ue=typeof BroadcastChannel<`u`,de=(0,V.jsxs)(`div`,{className:`editor-base__actions`,children:[T===`saving`&&(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Saving...`}),T===`saved`&&(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Saved`}),M.pendingComments.length>0&&(0,V.jsxs)(w,{variant:`ghost`,size:`sm`,onClick:M.sendComments,children:[`Send `,M.pendingComments.length,` comment`,M.pendingComments.length>1?`s`:``]}),g&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:$e,onClick:se,"aria-label":`Comment on cell`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:L,onClick:re,"aria-label":`Download CSV`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Le,onClick:ie,"aria-label":`Export PDF`}),!le&&ue&&!j.poppedOut&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Fe,onClick:j.handlePopout,"aria-label":`Pop out`}),!j.fullscreen&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Qe,onClick:j.handleFullscreen,"aria-label":`Full screen`}),j.fullscreen&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:nt,onClick:j.exitFullscreen,"aria-label":`Exit full screen`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:rt,onClick:j.handleMinimise,"aria-label":`Minimise`})]}),fe=(0,V.jsxs)(`div`,{className:`grid-editor__table-wrap`,onPaste:I,children:[M.showCommentInput?(0,V.jsxs)(`div`,{className:`grid-editor__comment-popover`,children:[(0,V.jsx)(`input`,{ref:M.commentInputRef,type:`text`,value:M.commentText,onChange:e=>M.setCommentText(e.target.value),onKeyDown:e=>{e.key===`Enter`&&ce(),e.key===`Escape`&&M.cancelComment()},placeholder:`Add a comment...`,className:`doc-editor__comment-input`,"aria-label":`Add comment on cell`}),(0,V.jsxs)(`div`,{className:`doc-editor__comment-actions`,children:[(0,V.jsx)(w,{variant:`primary`,size:`sm`,onClick:ce,disabled:!M.commentText.trim(),children:`Add`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:oe,onClick:M.cancelComment,"aria-label":`Cancel`})]})]}):null,(0,V.jsxs)(`table`,{className:`grid-editor__table`,role:`grid`,"aria-label":d,children:[(0,V.jsx)(`thead`,{children:(0,V.jsxs)(`tr`,{children:[(0,V.jsx)(`th`,{className:`grid-editor__row-header`}),f.map((e,t)=>(0,V.jsxs)(`th`,{className:`grid-editor__col-header`,style:{width:x[t]},children:[e,(0,V.jsx)(`div`,{className:`grid-editor__resize-handle`,onMouseDown:e=>ae(t,e),role:`separator`,"aria-orientation":`vertical`})]},t))]})}),(0,V.jsx)(`tbody`,{children:m.map((e,t)=>(0,V.jsxs)(`tr`,{children:[(0,V.jsxs)(`td`,{className:`grid-editor__row-header`,children:[(0,V.jsx)(`span`,{className:`grid-editor__row-num`,children:t+1}),m.length>1&&(0,V.jsx)(`button`,{className:`grid-editor__row-delete`,onClick:()=>F(t),"aria-label":`Delete row ${t+1}`,children:(0,V.jsx)(i,{size:12})})]}),e.map((e,n)=>{let r=g?.row===t&&g?.col===n;return(0,V.jsx)(`td`,{className:`grid-editor__cell ${r?`grid-editor__cell--active`:``}`,onClick:()=>!r&&N(t,n),role:`gridcell`,"aria-selected":r,style:{width:x[n]},children:r?(0,V.jsx)(`input`,{ref:b,type:`text`,className:`grid-editor__cell-input`,value:v,onChange:e=>y(e.target.value),onKeyDown:te,onBlur:P,"aria-label":`${f[n]} row ${t+1}`}):(0,V.jsx)(`span`,{className:`grid-editor__cell-text`,children:e})},n)})]},t))})]}),(0,V.jsxs)(`button`,{className:`grid-editor__add-row`,onClick:ne,"aria-label":`Add row`,children:[(0,V.jsx)(Oe,{size:14}),(0,V.jsx)(`span`,{children:`Add row`})]})]});return j.fullscreen?(0,V.jsxs)(`div`,{className:`editor-base-fullscreen`,children:[(0,V.jsxs)(`div`,{className:`editor-base-fullscreen__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base-fullscreen__title`,children:d}),de]}),(0,V.jsx)(`div`,{className:`editor-base-fullscreen__body`,style:{maxWidth:`none`},children:fe})]}):j.poppedOut?(0,V.jsx)(`div`,{className:`editor-base editor-base--popout`,children:(0,V.jsxs)(`div`,{className:`editor-base__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),(0,V.jsxs)(`div`,{className:`editor-base__actions`,children:[(0,V.jsx)(`span`,{className:`editor-base__status`,children:`Popped out`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:dt,onClick:j.handlePopIn,"aria-label":`Pop back in`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:rt,onClick:j.handleMinimise,"aria-label":`Close`})]})]})}):j.minimised?(0,V.jsx)(`div`,{className:`editor-base editor-base--minimised`,onClick:j.handleExpand,role:`button`,tabIndex:0,onKeyDown:e=>e.key===`Enter`&&j.handleExpand(),children:(0,V.jsxs)(`div`,{className:`editor-base__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),(0,V.jsxs)(`div`,{className:`editor-base__actions`,onClick:e=>e.stopPropagation(),children:[!le&&ue&&(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Fe,onClick:j.handlePopout,"aria-label":`Pop out`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:Qe,onClick:j.handleFullscreen,"aria-label":`Full screen`}),(0,V.jsx)(w,{variant:`ghost`,size:`sm`,icon:ct,onClick:j.handleExpand,"aria-label":`Expand`})]})]})}):(0,V.jsxs)(`div`,{className:`editor-base grid-editor`,children:[(0,V.jsxs)(`div`,{className:`editor-base__bar`,...j.dragBarProps,children:[(0,V.jsx)(`span`,{className:`editor-base__title`,children:d}),de]}),(0,V.jsx)(`div`,{className:`grid-editor__body`,children:fe})]})}var vT=Object.defineProperty,yT=Object.getOwnPropertySymbols,bT=Object.prototype.hasOwnProperty,xT=Object.prototype.propertyIsEnumerable,ST=(e,t,n)=>t in e?vT(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n,CT=(e,t)=>{for(var n in t||={})bT.call(t,n)&&ST(e,n,t[n]);if(yT)for(var n of yT(t))xT.call(t,n)&&ST(e,n,t[n]);return e},wT=(e,t)=>{var n={};for(var r in e)bT.call(e,r)&&t.indexOf(r)<0&&(n[r]=e[r]);if(e!=null&&yT)for(var r of yT(e))t.indexOf(r)<0&&xT.call(e,r)&&(n[r]=e[r]);return n},TT;(e=>{let t=class t{constructor(e,n,r,a){if(this.version=e,this.errorCorrectionLevel=n,this.modules=[],this.isFunction=[],e<t.MIN_VERSION||e>t.MAX_VERSION)throw RangeError(`Version value out of range`);if(a<-1||a>7)throw RangeError(`Mask value out of range`);this.size=e*4+17;let o=[];for(let e=0;e<this.size;e++)o.push(!1);for(let e=0;e<this.size;e++)this.modules.push(o.slice()),this.isFunction.push(o.slice());this.drawFunctionPatterns();let s=this.addEccAndInterleave(r);if(this.drawCodewords(s),a==-1){let e=1e9;for(let t=0;t<8;t++){this.applyMask(t),this.drawFormatBits(t);let n=this.getPenaltyScore();n<e&&(a=t,e=n),this.applyMask(t)}}i(0<=a&&a<=7),this.mask=a,this.applyMask(a),this.drawFormatBits(a),this.isFunction=[]}static encodeText(n,r){let i=e.QrSegment.makeSegments(n);return t.encodeSegments(i,r)}static encodeBinary(n,r){let i=e.QrSegment.makeBytes(n);return t.encodeSegments([i],r)}static encodeSegments(e,r,a=1,s=40,c=-1,l=!0){if(!(t.MIN_VERSION<=a&&a<=s&&s<=t.MAX_VERSION)||c<-1||c>7)throw RangeError(`Invalid value`);let u,d;for(u=a;;u++){let n=t.getNumDataCodewords(u,r)*8,i=o.getTotalBits(e,u);if(i<=n){d=i;break}if(u>=s)throw RangeError(`Data too long`)}for(let e of[t.Ecc.MEDIUM,t.Ecc.QUARTILE,t.Ecc.HIGH])l&&d<=t.getNumDataCodewords(u,e)*8&&(r=e);let f=[];for(let t of e){n(t.mode.modeBits,4,f),n(t.numChars,t.mode.numCharCountBits(u),f);for(let e of t.getData())f.push(e)}i(f.length==d);let p=t.getNumDataCodewords(u,r)*8;i(f.length<=p),n(0,Math.min(4,p-f.length),f),n(0,(8-f.length%8)%8,f),i(f.length%8==0);for(let e=236;f.length<p;e^=253)n(e,8,f);let m=[];for(;m.length*8<f.length;)m.push(0);return f.forEach((e,t)=>m[t>>>3]|=e<<7-(t&7)),new t(u,r,m,c)}getModule(e,t){return 0<=e&&e<this.size&&0<=t&&t<this.size&&this.modules[t][e]}getModules(){return this.modules}drawFunctionPatterns(){for(let e=0;e<this.size;e++)this.setFunctionModule(6,e,e%2==0),this.setFunctionModule(e,6,e%2==0);this.drawFinderPattern(3,3),this.drawFinderPattern(this.size-4,3),this.drawFinderPattern(3,this.size-4);let e=this.getAlignmentPatternPositions(),t=e.length;for(let n=0;n<t;n++)for(let r=0;r<t;r++)n==0&&r==0||n==0&&r==t-1||n==t-1&&r==0||this.drawAlignmentPattern(e[n],e[r]);this.drawFormatBits(0),this.drawVersion()}drawFormatBits(e){let t=this.errorCorrectionLevel.formatBits<<3|e,n=t;for(let e=0;e<10;e++)n=n<<1^(n>>>9)*1335;let a=(t<<10|n)^21522;i(a>>>15==0);for(let e=0;e<=5;e++)this.setFunctionModule(8,e,r(a,e));this.setFunctionModule(8,7,r(a,6)),this.setFunctionModule(8,8,r(a,7)),this.setFunctionModule(7,8,r(a,8));for(let e=9;e<15;e++)this.setFunctionModule(14-e,8,r(a,e));for(let e=0;e<8;e++)this.setFunctionModule(this.size-1-e,8,r(a,e));for(let e=8;e<15;e++)this.setFunctionModule(8,this.size-15+e,r(a,e));this.setFunctionModule(8,this.size-8,!0)}drawVersion(){if(this.version<7)return;let e=this.version;for(let t=0;t<12;t++)e=e<<1^(e>>>11)*7973;let t=this.version<<12|e;i(t>>>18==0);for(let e=0;e<18;e++){let n=r(t,e),i=this.size-11+e%3,a=Math.floor(e/3);this.setFunctionModule(i,a,n),this.setFunctionModule(a,i,n)}}drawFinderPattern(e,t){for(let n=-4;n<=4;n++)for(let r=-4;r<=4;r++){let i=Math.max(Math.abs(r),Math.abs(n)),a=e+r,o=t+n;0<=a&&a<this.size&&0<=o&&o<this.size&&this.setFunctionModule(a,o,i!=2&&i!=4)}}drawAlignmentPattern(e,t){for(let n=-2;n<=2;n++)for(let r=-2;r<=2;r++)this.setFunctionModule(e+r,t+n,Math.max(Math.abs(r),Math.abs(n))!=1)}setFunctionModule(e,t,n){this.modules[t][e]=n,this.isFunction[t][e]=!0}addEccAndInterleave(e){let n=this.version,r=this.errorCorrectionLevel;if(e.length!=t.getNumDataCodewords(n,r))throw RangeError(`Invalid argument`);let a=t.NUM_ERROR_CORRECTION_BLOCKS[r.ordinal][n],o=t.ECC_CODEWORDS_PER_BLOCK[r.ordinal][n],s=Math.floor(t.getNumRawDataModules(n)/8),c=a-s%a,l=Math.floor(s/a),u=[],d=t.reedSolomonComputeDivisor(o);for(let n=0,r=0;n<a;n++){let i=e.slice(r,r+l-o+(n<c?0:1));r+=i.length;let a=t.reedSolomonComputeRemainder(i,d);n<c&&i.push(0),u.push(i.concat(a))}let f=[];for(let e=0;e<u[0].length;e++)u.forEach((t,n)=>{(e!=l-o||n>=c)&&f.push(t[e])});return i(f.length==s),f}drawCodewords(e){if(e.length!=Math.floor(t.getNumRawDataModules(this.version)/8))throw RangeError(`Invalid argument`);let n=0;for(let t=this.size-1;t>=1;t-=2){t==6&&(t=5);for(let i=0;i<this.size;i++)for(let a=0;a<2;a++){let o=t-a,s=t+1&2?i:this.size-1-i;!this.isFunction[s][o]&&n<e.length*8&&(this.modules[s][o]=r(e[n>>>3],7-(n&7)),n++)}}i(n==e.length*8)}applyMask(e){if(e<0||e>7)throw RangeError(`Mask value out of range`);for(let t=0;t<this.size;t++)for(let n=0;n<this.size;n++){let r;switch(e){case 0:r=(n+t)%2==0;break;case 1:r=t%2==0;break;case 2:r=n%3==0;break;case 3:r=(n+t)%3==0;break;case 4:r=(Math.floor(n/3)+Math.floor(t/2))%2==0;break;case 5:r=n*t%2+n*t%3==0;break;case 6:r=(n*t%2+n*t%3)%2==0;break;case 7:r=((n+t)%2+n*t%3)%2==0;break;default:throw Error(`Unreachable`)}!this.isFunction[t][n]&&r&&(this.modules[t][n]=!this.modules[t][n])}}getPenaltyScore(){let e=0;for(let n=0;n<this.size;n++){let r=!1,i=0,a=[0,0,0,0,0,0,0];for(let o=0;o<this.size;o++)this.modules[n][o]==r?(i++,i==5?e+=t.PENALTY_N1:i>5&&e++):(this.finderPenaltyAddHistory(i,a),r||(e+=this.finderPenaltyCountPatterns(a)*t.PENALTY_N3),r=this.modules[n][o],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,a)*t.PENALTY_N3}for(let n=0;n<this.size;n++){let r=!1,i=0,a=[0,0,0,0,0,0,0];for(let o=0;o<this.size;o++)this.modules[o][n]==r?(i++,i==5?e+=t.PENALTY_N1:i>5&&e++):(this.finderPenaltyAddHistory(i,a),r||(e+=this.finderPenaltyCountPatterns(a)*t.PENALTY_N3),r=this.modules[o][n],i=1);e+=this.finderPenaltyTerminateAndCount(r,i,a)*t.PENALTY_N3}for(let n=0;n<this.size-1;n++)for(let r=0;r<this.size-1;r++){let i=this.modules[n][r];i==this.modules[n][r+1]&&i==this.modules[n+1][r]&&i==this.modules[n+1][r+1]&&(e+=t.PENALTY_N2)}let n=0;for(let e of this.modules)n=e.reduce((e,t)=>e+(t?1:0),n);let r=this.size*this.size,a=Math.ceil(Math.abs(n*20-r*10)/r)-1;return i(0<=a&&a<=9),e+=a*t.PENALTY_N4,i(0<=e&&e<=2568888),e}getAlignmentPatternPositions(){if(this.version==1)return[];{let e=Math.floor(this.version/7)+2,t=this.version==32?26:Math.ceil((this.version*4+4)/(e*2-2))*2,n=[6];for(let r=this.size-7;n.length<e;r-=t)n.splice(1,0,r);return n}}static getNumRawDataModules(e){if(e<t.MIN_VERSION||e>t.MAX_VERSION)throw RangeError(`Version number out of range`);let n=(16*e+128)*e+64;if(e>=2){let t=Math.floor(e/7)+2;n-=(25*t-10)*t-55,e>=7&&(n-=36)}return i(208<=n&&n<=29648),n}static getNumDataCodewords(e,n){return Math.floor(t.getNumRawDataModules(e)/8)-t.ECC_CODEWORDS_PER_BLOCK[n.ordinal][e]*t.NUM_ERROR_CORRECTION_BLOCKS[n.ordinal][e]}static reedSolomonComputeDivisor(e){if(e<1||e>255)throw RangeError(`Degree out of range`);let n=[];for(let t=0;t<e-1;t++)n.push(0);n.push(1);let r=1;for(let i=0;i<e;i++){for(let e=0;e<n.length;e++)n[e]=t.reedSolomonMultiply(n[e],r),e+1<n.length&&(n[e]^=n[e+1]);r=t.reedSolomonMultiply(r,2)}return n}static reedSolomonComputeRemainder(e,n){let r=n.map(e=>0);for(let i of e){let e=i^r.shift();r.push(0),n.forEach((n,i)=>r[i]^=t.reedSolomonMultiply(n,e))}return r}static reedSolomonMultiply(e,t){if(e>>>8||t>>>8)throw RangeError(`Byte out of range`);let n=0;for(let r=7;r>=0;r--)n=n<<1^(n>>>7)*285,n^=(t>>>r&1)*e;return i(n>>>8==0),n}finderPenaltyCountPatterns(e){let t=e[1];i(t<=this.size*3);let n=t>0&&e[2]==t&&e[3]==t*3&&e[4]==t&&e[5]==t;return(n&&e[0]>=t*4&&e[6]>=t?1:0)+(n&&e[6]>=t*4&&e[0]>=t?1:0)}finderPenaltyTerminateAndCount(e,t,n){return e&&(this.finderPenaltyAddHistory(t,n),t=0),t+=this.size,this.finderPenaltyAddHistory(t,n),this.finderPenaltyCountPatterns(n)}finderPenaltyAddHistory(e,t){t[0]==0&&(e+=this.size),t.pop(),t.unshift(e)}};t.MIN_VERSION=1,t.MAX_VERSION=40,t.PENALTY_N1=3,t.PENALTY_N2=3,t.PENALTY_N3=40,t.PENALTY_N4=10,t.ECC_CODEWORDS_PER_BLOCK=[[-1,7,10,15,20,26,18,20,24,30,18,20,24,26,30,22,24,28,30,28,28,28,28,30,30,26,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,10,16,26,18,24,16,18,22,22,26,30,22,22,24,24,28,28,26,26,26,26,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28],[-1,13,22,18,26,18,24,18,22,20,24,28,26,24,20,30,24,28,28,26,30,28,30,30,30,30,28,30,30,30,30,30,30,30,30,30,30,30,30,30,30],[-1,17,28,22,16,22,28,26,26,24,28,24,28,22,24,24,30,28,28,26,28,30,24,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30]],t.NUM_ERROR_CORRECTION_BLOCKS=[[-1,1,1,1,1,1,2,2,2,2,4,4,4,4,4,6,6,6,6,7,8,8,9,9,10,12,12,12,13,14,15,16,17,18,19,19,20,21,22,24,25],[-1,1,1,1,2,2,4,4,4,5,5,5,8,9,9,10,10,11,13,14,16,17,17,18,20,21,23,25,26,28,29,31,33,35,37,38,40,43,45,47,49],[-1,1,1,2,2,4,4,6,6,8,8,8,10,12,16,12,17,16,18,21,20,23,23,25,27,29,34,34,35,38,40,43,45,48,51,53,56,59,62,65,68],[-1,1,1,2,4,4,4,5,6,8,8,11,11,16,16,18,16,19,21,25,25,25,34,30,32,35,37,40,42,45,48,51,54,57,60,63,66,70,74,77,81]],e.QrCode=t;function n(e,t,n){if(t<0||t>31||e>>>t)throw RangeError(`Value out of range`);for(let r=t-1;r>=0;r--)n.push(e>>>r&1)}function r(e,t){return(e>>>t&1)!=0}function i(e){if(!e)throw Error(`Assertion error`)}let a=class e{constructor(e,t,n){if(this.mode=e,this.numChars=t,this.bitData=n,t<0)throw RangeError(`Invalid argument`);this.bitData=n.slice()}static makeBytes(t){let r=[];for(let e of t)n(e,8,r);return new e(e.Mode.BYTE,t.length,r)}static makeNumeric(t){if(!e.isNumeric(t))throw RangeError(`String contains non-numeric characters`);let r=[];for(let e=0;e<t.length;){let i=Math.min(t.length-e,3);n(parseInt(t.substring(e,e+i),10),i*3+1,r),e+=i}return new e(e.Mode.NUMERIC,t.length,r)}static makeAlphanumeric(t){if(!e.isAlphanumeric(t))throw RangeError(`String contains unencodable characters in alphanumeric mode`);let r=[],i;for(i=0;i+2<=t.length;i+=2){let a=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(i))*45;a+=e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(i+1)),n(a,11,r)}return i<t.length&&n(e.ALPHANUMERIC_CHARSET.indexOf(t.charAt(i)),6,r),new e(e.Mode.ALPHANUMERIC,t.length,r)}static makeSegments(t){return t==``?[]:e.isNumeric(t)?[e.makeNumeric(t)]:e.isAlphanumeric(t)?[e.makeAlphanumeric(t)]:[e.makeBytes(e.toUtf8ByteArray(t))]}static makeEci(t){let r=[];if(t<0)throw RangeError(`ECI assignment value out of range`);if(t<128)n(t,8,r);else if(t<16384)n(2,2,r),n(t,14,r);else if(t<1e6)n(6,3,r),n(t,21,r);else throw RangeError(`ECI assignment value out of range`);return new e(e.Mode.ECI,0,r)}static isNumeric(t){return e.NUMERIC_REGEX.test(t)}static isAlphanumeric(t){return e.ALPHANUMERIC_REGEX.test(t)}getData(){return this.bitData.slice()}static getTotalBits(e,t){let n=0;for(let r of e){let e=r.mode.numCharCountBits(t);if(r.numChars>=1<<e)return 1/0;n+=4+e+r.bitData.length}return n}static toUtf8ByteArray(e){e=encodeURI(e);let t=[];for(let n=0;n<e.length;n++)e.charAt(n)==`%`?(t.push(parseInt(e.substring(n+1,n+3),16)),n+=2):t.push(e.charCodeAt(n));return t}};a.NUMERIC_REGEX=/^[0-9]*$/,a.ALPHANUMERIC_REGEX=/^[A-Z0-9 $%*+.\/:-]*$/,a.ALPHANUMERIC_CHARSET=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`;let o=a;e.QrSegment=a})(TT||={}),(e=>{(e=>{let t=class{constructor(e,t){this.ordinal=e,this.formatBits=t}};t.LOW=new t(0,1),t.MEDIUM=new t(1,0),t.QUARTILE=new t(2,3),t.HIGH=new t(3,2),e.Ecc=t})(e.QrCode||={})})(TT||={}),(e=>{(e=>{let t=class{constructor(e,t){this.modeBits=e,this.numBitsCharCount=t}numCharCountBits(e){return this.numBitsCharCount[Math.floor((e+7)/17)]}};t.NUMERIC=new t(1,[10,12,14]),t.ALPHANUMERIC=new t(2,[9,11,13]),t.BYTE=new t(4,[8,16,16]),t.KANJI=new t(8,[8,10,12]),t.ECI=new t(7,[0,0,0]),e.Mode=t})(e.QrSegment||={})})(TT||={});var ET=TT,DT={L:ET.QrCode.Ecc.LOW,M:ET.QrCode.Ecc.MEDIUM,Q:ET.QrCode.Ecc.QUARTILE,H:ET.QrCode.Ecc.HIGH},OT=128,kT=`L`,AT=`#FFFFFF`,jT=`#000000`,MT=!1,NT=1,PT=4,FT=0,IT=.1;function LT(e,t=0){let n=[];return e.forEach(function(e,r){let i=null;e.forEach(function(a,o){if(!a&&i!==null){n.push(`M${i+t} ${r+t}h${o-i}v1H${i+t}z`),i=null;return}if(o===e.length-1){if(!a)return;i===null?n.push(`M${o+t},${r+t} h1v1H${o+t}z`):n.push(`M${i+t},${r+t} h${o+1-i}v1H${i+t}z`);return}a&&i===null&&(i=o)})}),n.join(``)}function RT(e,t){return e.slice().map((e,n)=>n<t.y||n>=t.y+t.h?e:e.map((e,n)=>n<t.x||n>=t.x+t.w?e:!1))}function zT(e,t,n,r){if(r==null)return null;let i=e.length+n*2,a=Math.floor(t*IT),o=i/t,s=(r.width||a)*o,c=(r.height||a)*o,l=r.x==null?e.length/2-s/2:r.x*o,u=r.y==null?e.length/2-c/2:r.y*o,d=r.opacity==null?1:r.opacity,f=null;if(r.excavate){let e=Math.floor(l),t=Math.floor(u);f={x:e,y:t,w:Math.ceil(s+l-e),h:Math.ceil(c+u-t)}}let p=r.crossOrigin;return{x:l,y:u,h:c,w:s,excavation:f,opacity:d,crossOrigin:p}}function BT(e,t){return t==null?e?PT:FT:Math.max(Math.floor(t),0)}function VT({value:e,level:t,minVersion:n,includeMargin:r,marginSize:i,imageSettings:a,size:o,boostLevel:s}){let c=B.useMemo(()=>{let r=(Array.isArray(e)?e:[e]).reduce((e,t)=>(e.push(...ET.QrSegment.makeSegments(t)),e),[]);return ET.QrCode.encodeSegments(r,DT[t],n,void 0,void 0,s)},[e,t,n,s]),{cells:l,margin:u,numCells:d,calculatedImageSettings:f}=B.useMemo(()=>{let e=c.getModules(),t=BT(r,i);return{cells:e,margin:t,numCells:e.length+t*2,calculatedImageSettings:zT(e,o,t,a)}},[c,o,a,r,i]);return{qrcode:c,margin:u,cells:l,numCells:d,calculatedImageSettings:f}}var HT=function(){try{new Path2D().addPath(new Path2D)}catch{return!1}return!0}(),UT=B.forwardRef(function(e,t){let n=e,{value:r,size:i=OT,level:a=kT,bgColor:o=AT,fgColor:s=jT,includeMargin:c=MT,minVersion:l=NT,boostLevel:u,marginSize:d,imageSettings:f}=n,p=wT(n,[`value`,`size`,`level`,`bgColor`,`fgColor`,`includeMargin`,`minVersion`,`boostLevel`,`marginSize`,`imageSettings`]),{style:m}=p,h=wT(p,[`style`]),g=f?.src,_=B.useRef(null),v=B.useRef(null),y=B.useCallback(e=>{_.current=e,typeof t==`function`?t(e):t&&(t.current=e)},[t]),[b,x]=B.useState(!1),{margin:S,cells:C,numCells:w,calculatedImageSettings:T}=VT({value:r,level:a,minVersion:l,boostLevel:u,includeMargin:c,marginSize:d,imageSettings:f,size:i});B.useEffect(()=>{if(_.current!=null){let e=_.current,t=e.getContext(`2d`);if(!t)return;let n=C,r=v.current,a=T!=null&&r!==null&&r.complete&&r.naturalHeight!==0&&r.naturalWidth!==0;a&&T.excavation!=null&&(n=RT(C,T.excavation));let c=window.devicePixelRatio||1;e.height=e.width=i*c;let l=i/w*c;t.scale(l,l),t.fillStyle=o,t.fillRect(0,0,w,w),t.fillStyle=s,HT?t.fill(new Path2D(LT(n,S))):C.forEach(function(e,n){e.forEach(function(e,r){e&&t.fillRect(r+S,n+S,1,1)})}),T&&(t.globalAlpha=T.opacity),a&&t.drawImage(r,T.x+S,T.y+S,T.w,T.h)}}),B.useEffect(()=>{x(!1)},[g]);let E=CT({height:i,width:i},m),D=null;return g!=null&&(D=B.createElement(`img`,{src:g,key:g,style:{display:`none`},onLoad:()=>{x(!0)},ref:v,crossOrigin:T?.crossOrigin})),B.createElement(B.Fragment,null,B.createElement(`canvas`,CT({style:E,height:i,width:i,ref:y,role:`img`},h)),D)});UT.displayName=`QRCodeCanvas`;var WT=B.forwardRef(function(e,t){let n=e,{value:r,size:i=OT,level:a=kT,bgColor:o=AT,fgColor:s=jT,includeMargin:c=MT,minVersion:l=NT,boostLevel:u,title:d,marginSize:f,imageSettings:p}=n,m=wT(n,[`value`,`size`,`level`,`bgColor`,`fgColor`,`includeMargin`,`minVersion`,`boostLevel`,`title`,`marginSize`,`imageSettings`]),{margin:h,cells:g,numCells:_,calculatedImageSettings:v}=VT({value:r,level:a,minVersion:l,boostLevel:u,includeMargin:c,marginSize:f,imageSettings:p,size:i}),y=g,b=null;p!=null&&v!=null&&(v.excavation!=null&&(y=RT(g,v.excavation)),b=B.createElement(`image`,{href:p.src,height:v.h,width:v.w,x:v.x+h,y:v.y+h,preserveAspectRatio:`none`,opacity:v.opacity,crossOrigin:v.crossOrigin}));let x=LT(y,h);return B.createElement(`svg`,CT({height:i,width:i,viewBox:`0 0 ${_} ${_}`,ref:t,role:`img`},m),!!d&&B.createElement(`title`,null,d),B.createElement(`path`,{fill:o,d:`M0,0 h${_}v${_}H0z`,shapeRendering:`crispEdges`}),B.createElement(`path`,{fill:s,d:x,shapeRendering:`crispEdges`}),b)});WT.displayName=`QRCodeSVG`;function GT({data:e,onSubmit:t,submitted:n}){let r=e,i=(0,B.useRef)(!1),a=r?.value?.trim()??``;return(0,B.useEffect)(()=>{a&&!n&&!i.current&&(i.current=!0,t(``))},[a,n,t]),a?(0,V.jsxs)(`div`,{className:`qr-code`,children:[(0,V.jsx)(`div`,{className:`qr-code__canvas`,children:(0,V.jsx)(WT,{value:a,size:240,level:`M`,bgColor:`#ffffff`,fgColor:`#000000`})}),r?.label&&(0,V.jsx)(`div`,{className:`qr-code__label`,children:r.label})]}):(0,V.jsx)(`div`,{className:`qr-code qr-code--error`,children:`No QR data provided. Try again or ask the agent to generate a new QR code.`})}function KT(e){return e===0?`0 B`:e<1024?`${e} B`:e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/(1024*1024)).toFixed(1)} MB`}function qT(e){return e.startsWith(`image/`)?(0,V.jsx)(ze,{size:18}):e===`application/pdf`?(0,V.jsx)(c,{size:18}):e===`text/calendar`?(0,V.jsx)(Te,{size:18}):e.startsWith(`text/`)?(0,V.jsx)(E,{size:18}):e===`application/zip`||e===`application/x-zip-compressed`?(0,V.jsx)(Ie,{size:18}):(0,V.jsx)(le,{size:18})}function JT({data:e,onSubmit:t,submitted:n,sessionKey:r}){let i=e,a=(0,B.useRef)(!1),o=!!(i?.attachmentId&&i?.filename);(0,B.useEffect)(()=>{o&&!n&&!a.current&&(a.current=!0,t(JSON.stringify({_lifecycle:!0,component:`file-attachment`,event:`rendered`,attachmentId:i.attachmentId,filename:i.filename})))},[o,n,t,i]);let s=(0,B.useCallback)(()=>{if(!i?.attachmentId||!r)return;let e=`/api/admin/attachment/${i.attachmentId}?session_key=${encodeURIComponent(r)}`,t=document.createElement(`a`);t.href=e,t.download=i.filename,t.click()},[i,r]);return o?(0,V.jsx)(O,{submitted:!1,submittedLabel:`Downloaded`,actions:[{label:`Download`,variant:`primary`,onClick:s,disabled:!r}],children:(0,V.jsxs)(`div`,{className:`file-attachment`,children:[(0,V.jsx)(`div`,{className:`file-attachment__icon`,children:qT(i.mimeType)}),(0,V.jsxs)(`div`,{className:`file-attachment__info`,children:[(0,V.jsx)(`div`,{className:`file-attachment__name`,title:i.filename,children:i.filename}),(0,V.jsxs)(`div`,{className:`file-attachment__meta`,children:[KT(i.sizeBytes),` · `,i.mimeType]})]}),(0,V.jsx)(`button`,{className:`file-attachment__download`,onClick:s,disabled:!r,title:`Download file`,children:(0,V.jsx)(L,{size:16})})]})}):null}var YT=/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/,XT=/^[a-z0-9-]$/;function ZT(e){return e?YT.test(e):!1}function QT(e){let t=e.toLowerCase(),n=``;for(let e of t)XT.test(e)&&(n+=e);for(;n.startsWith(`-`);)n=n.slice(1);return n}function $T(e){return[{key:`length`,label:`At least 8 characters`,met:e.length>=8},{key:`number`,label:`Contains a number`,met:/\d/.test(e)},{key:`special`,label:`Contains a special character`,met:/[^A-Za-z0-9]/.test(e)},{key:`whitespace`,label:`No spaces`,met:e.length>0&&!/\s/.test(e)}]}var eE=/https:\/\/dash\.cloudflare\.com\/argotunnel\?[^\s]+/,tE=/^OAUTH_URL:\s*(\S+)/,nE=/result=error\s+reason=([a-z0-9-]+)/;function rE(e){let t=e.match(tE);if(t)return t[1];let n=e.match(eE);return n?n[0]:null}function iE(e){let t=e.match(nE);return t?t[1]:null}var aE=/\[(\d+)\/(\d+)\]/,oE=2e3,sE=9e4;function cE(e){if(!e)return null;let t=e.match(aE);return t?parseInt(t[1],10):null}function lE({actionId:e,sessionKey:t,onExit:n,maxLines:r=1e3,selfRestart:i}){let[a,o]=(0,B.useState)([]),[s,c]=(0,B.useState)(null),[u,d]=(0,B.useState)(null),[f,p]=(0,B.useState)(null),[m,h]=(0,B.useState)(null),[g,_]=(0,B.useState)({phase:`idle`}),[v,y]=(0,B.useState)(!1),[b,x]=(0,B.useState)(null),S=(0,B.useRef)(null),C=(0,B.useRef)(null),w=(0,B.useRef)(0),T=(0,B.useRef)(null),E=(0,B.useRef)(null),O=(0,B.useRef)(null),k=(0,B.useRef)(null),A=(0,B.useRef)(null),j=(0,B.useRef)(null),M=(0,B.useRef)(n);M.current=n;let N=(0,B.useCallback)(()=>{A.current&&=(clearInterval(A.current),null),j.current&&=(clearTimeout(j.current),null)},[]),P=(0,B.useCallback)(e=>{e&&(S.current=e,x(e))},[]),ee=(0,B.useCallback)(async()=>{if(m){_({phase:`respawning`});try{let e=await fetch(`/api/admin/device-browser/navigate`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({url:m,intent:`open Cloudflare authorisation page`,hostname:``})}),t=await e.json().catch(()=>({}));if(e.ok&&t.ok){_({phase:`respawned`}),window.setTimeout(()=>_({phase:`idle`}),2500);return}_({phase:`failed`,reason:(t.navigateResult??t.detail??`navigation failed`).slice(0,80)})}catch(e){_({phase:`failed`,reason:(e instanceof Error?e.message:String(e)).slice(0,80)})}}},[m]),te=(0,B.useCallback)(()=>{let n=w.current>0?`&from=${w.current}`:``,a=new EventSource(`/api/admin/actions/${encodeURIComponent(e)}/stream?session_key=${encodeURIComponent(t)}${n}`);T.current=a,a.addEventListener(`line`,e=>{try{let t=JSON.parse(e.data);if(w.current=t.byteOffset,o(e=>{let n=[...e,t];return n.length>r&&n.splice(0,n.length-r),n}),!m){let e=rE(t.text);e&&h(e)}let n=t.text.match(aE);n&&P(n[0]);let i=iE(t.text);i&&(C.current=i),p(null)}catch(e){console.error(`[ActionLogPanel] line parse failed:`,e)}}),a.addEventListener(`heartbeat`,e=>{try{let t=JSON.parse(e.data);O.current=t,c(t),P(t.last_phase),p(null)}catch(e){console.error(`[ActionLogPanel] heartbeat parse failed:`,e)}}),a.addEventListener(`exit`,e=>{try{let t=JSON.parse(e.data);d(t),M.current?.(t,C.current)}catch(e){console.error(`[ActionLogPanel] exit parse failed:`,e)}finally{a.close(),T.current=null,N(),y(!1)}}),a.onerror=()=>{if(u)return;a.close(),T.current=null;let e=cE(S.current);if(i&&e!==null&&e>=i.atPhase&&k.current!==null){console.log(`[ActionLogPanel] SSE drop during self-restart window (phase=${S.current}) — banner suppressed`),p(null),y(!0),ne();return}p(`Connection lost — reconnecting…`),setTimeout(()=>{u||te()},1500)}},[e,t,r,i?.atPhase,N]);function ne(){N(),A.current=setInterval(async()=>{try{let e=await fetch(`/api/admin/version`,{cache:`no-store`});if(!e.ok)return;let t=(await e.json())?.installed,n=k.current;t&&n&&t!==n&&(console.log(`[ActionLogPanel] admin-server version changed ${n}→${t} — reconnecting stream`),N(),y(!1),k.current=t,te())}catch{}},oE),j.current=setTimeout(()=>{console.log(`[ActionLogPanel] self-restart window timeout (${sE}ms) — reverting to error banner`),N(),y(!1),p(`New version did not come up — reconnecting…`),setTimeout(()=>{u||te()},1500)},sE)}(0,B.useEffect)(()=>(te(),()=>{T.current?.close(),T.current=null,N()}),[te,N]),(0,B.useEffect)(()=>{if(!i)return;let e=!1;return(async()=>{try{let t=await fetch(`/api/admin/version`,{cache:`no-store`});if(!t.ok)return;let n=await t.json();!e&&typeof n?.installed==`string`&&(k.current=n.installed)}catch{}})(),()=>{e=!0}},[e,i?.atPhase]),(0,B.useEffect)(()=>{let e=E.current;e&&e.scrollHeight-e.scrollTop-e.clientHeight<80&&(e.scrollTop=e.scrollHeight)},[a]);let F=e=>new Date(e).toLocaleTimeString(void 0,{hour12:!1}),I=s?Math.floor(s.elapsed_since_last_line_ms/1e3):0,re=!u&&(f!==null||v),ie=re?`#666`:void 0,ae=!u&&!re&&s&&I>=5;return(0,V.jsxs)(`div`,{className:`action-log-panel`,style:{display:`flex`,flexDirection:`column`,height:`100%`,background:`#0a0a0a`,color:`#e8e8e8`,borderRadius:4,overflow:`hidden`,fontFamily:`ui-monospace, monospace`,fontSize:12},children:[(0,V.jsxs)(`div`,{style:{padding:`6px 10px`,borderBottom:`1px solid #222`,display:`flex`,gap:12,alignItems:`center`,flexWrap:`wrap`,fontSize:11},children:[(0,V.jsx)(`span`,{style:{color:`#888`},children:`action:`}),(0,V.jsx)(`span`,{children:e}),s&&(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{style:{color:`#888`,marginLeft:`auto`},children:`state:`}),(0,V.jsx)(`span`,{style:{color:ie},children:s.systemd_state}),b&&(0,V.jsxs)(V.Fragment,{children:[(0,V.jsx)(`span`,{style:{color:`#888`},children:`phase:`}),(0,V.jsx)(`span`,{style:{color:ie},children:b})]}),re&&(0,V.jsx)(`span`,{style:{color:`#888`,fontStyle:`italic`},children:`(stale)`}),ae&&(0,V.jsxs)(`span`,{style:{color:`#e4a657`,display:`inline-flex`,alignItems:`center`,gap:4},children:[(0,V.jsx)(l,{size:11}),` silent `,I,`s`]})]})]}),m&&!u&&(0,V.jsxs)(`div`,{style:{padding:`8px 10px`,borderBottom:`1px solid #222`,background:`#1b1f2b`,display:`flex`,alignItems:`center`,gap:8,flexWrap:`wrap`},children:[(0,V.jsx)(`span`,{style:{color:`#cbd5f5`},children:`Authorise this device on the Pi’s browser — click the zone, then Authorize.`}),(0,V.jsxs)(`button`,{type:`button`,onClick:ee,disabled:g.phase===`respawning`,style:{marginLeft:`auto`,color:`#fff`,background:`#f6821f`,padding:`4px 10px`,borderRadius:3,border:`none`,cursor:g.phase===`respawning`?`wait`:`pointer`,display:`inline-flex`,alignItems:`center`,gap:6,fontSize:11,opacity:g.phase===`respawning`?.7:1},title:`Re-opens the Cloudflare page on the Pi's VNC browser`,children:[(0,V.jsx)(D,{size:12}),g.phase===`respawning`?`Re-opening on Pi…`:g.phase===`respawned`?`Re-opened on Pi`:`Re-open on Pi browser`]}),g.phase===`failed`&&(0,V.jsxs)(`span`,{style:{width:`100%`,color:`#ffc987`,fontSize:11,marginTop:4},children:[`Could not re-open on Pi: `,g.reason,`. The page should still be on the Pi VNC from the initial spawn.`]})]}),f&&!v&&(0,V.jsxs)(`div`,{style:{padding:`6px 10px`,background:`#3a2b10`,color:`#ffc987`,display:`flex`,alignItems:`center`,gap:8},children:[(0,V.jsx)(ge,{size:12,className:`spin`}),` `,f]}),v&&(0,V.jsxs)(`div`,{style:{padding:`6px 10px`,background:`#132319`,color:`#9aeab0`,display:`flex`,alignItems:`center`,gap:8},children:[(0,V.jsx)(ge,{size:12,className:`spin`}),` Waiting for new version…`]}),(0,V.jsxs)(`div`,{ref:E,style:{flex:1,overflow:`auto`,padding:`6px 10px`,lineHeight:1.45,whiteSpace:`pre-wrap`,wordBreak:`break-word`},children:[a.length===0&&!u&&(0,V.jsx)(`div`,{style:{color:`#888`},children:`Waiting for output…`}),a.map(e=>(0,V.jsxs)(`div`,{style:{display:`flex`,gap:10,color:e.stream===`stderr`?`#ff9999`:`#e8e8e8`},children:[(0,V.jsx)(`span`,{style:{color:`#666`,flexShrink:0},children:F(e.ts)}),(0,V.jsx)(`span`,{children:e.text})]},e.byteOffset))]}),u&&(0,V.jsxs)(`div`,{style:{padding:`8px 10px`,borderTop:`1px solid #222`,background:u.code===0?`#14331b`:`#3a1717`,color:u.code===0?`#9aeab0`:`#ff9999`,display:`flex`,alignItems:`center`,gap:8},children:[u.code===0?(0,V.jsx)(De,{size:13}):(0,V.jsx)(l,{size:13}),(0,V.jsxs)(`span`,{children:[u.code===0?`Completed`:`Failed (exit ${u.code})`,` · `,Math.round(u.duration_ms/1e3),`s`]})]})]})}var uE={title:`Authorise the tunnel in your own browser`,intro:`The Pi's browser couldn't drive the Cloudflare consent page automatically. Open the dashboard in your own browser and follow these steps:`,steps:[`The "Authorize Cloudflare Tunnel" page shows every zone on the currently-active account.`,`Click the zone you want the tunnel to be able to route DNS for.`,`Click Authorize. The browser closes or shows "You can close this page."`,`Return here and click "Try again" — the cert will land and setup will continue.`],footnote:`If the account shown in the top-left of the dashboard is not the one that owns your zone, click the account-name dropdown first and switch accounts before authorising.`},dE=18e4,fE=4096;function pE(e){let t=new TextEncoder().encode(e);return t.length<=fE?e:`${new TextDecoder(`utf-8`,{fatal:!1}).decode(t.slice(0,fE))}\n… ${t.length-fE} bytes truncated (total ${t.length} bytes)`}function mE({data:e,onSubmit:t,submitted:n,sessionKey:r}){let i=e,[a,o]=(0,B.useState)({status:`loading`}),[s,c]=(0,B.useState)(``),[l,u]=(0,B.useState)(``),[d,f]=(0,B.useState)(``),[p,m]=(0,B.useState)(``),[h,g]=(0,B.useState)(``),[_,v]=(0,B.useState)(``),[y,b]=(0,B.useState)(!1),[x,S]=(0,B.useState)(!1),[C,w]=(0,B.useState)(!1),[T,E]=(0,B.useState)(null),[D,k]=(0,B.useState)(null),[A,j]=(0,B.useState)(null),M=(0,B.useRef)(null),N=(0,B.useRef)(new Set),P=(0,B.useCallback)(async()=>{if(!r){o({status:`error`,field:`session`,message:`No session key available — refresh the page.`});return}o({status:`loading`});try{let e=await(await fetch(`/api/admin/cloudflare/domains?session_key=${encodeURIComponent(r)}`,{method:`GET`,headers:{Accept:`application/json`}})).json();if(!e.ok){o({status:`error`,field:e.field,message:e.message,output:e.output,correlationId:e.correlationId,streamLogPath:e.streamLogPath});return}if(e.domains.length===0){o({status:`empty`});return}o({status:`populated`,domains:e.domains}),u(t=>t||e.domains[0]),m(t=>t||e.domains[0])}catch(e){o({status:`error`,field:`script`,message:e instanceof Error?e.message:`Network error loading domains.`})}},[r]);(0,B.useEffect)(()=>{P()},[P]),(0,B.useEffect)(()=>()=>{M.current&&clearTimeout(M.current)},[]);let ee=a.status===`populated`?a.domains:[],te=e=>{n||x||(c(QT(e)),j(null))},ne=e=>{n||x||(f(QT(e)),j(null))},F=ZT(s)&&l.length>0,I=d!==``,re=!I||ZT(d)&&p.length>0,ie=F?`${s.replace(/-+$/,``)}.${l}`:``,ae=I&&re?`${d.replace(/-+$/,``)}.${p}`:``,oe=!(ae&&ae===ie),se=$T(_),L=se.every(e=>e.met),ce=a.status===`populated`&&F&&re&&oe&&L&&!x,le=s!==``&&!ZT(s)?`Use lowercase letters, numbers, and hyphens. Start and end with a letter or number.`:null,ue=I&&!ZT(d)?`Use lowercase letters, numbers, and hyphens. Start and end with a letter or number.`:oe?null:`Public address must differ from the admin address.`,de=A?.field===`admin`?A.message:null,fe=A?.field===`public`?A.message:null,pe=A?.field===`password`?A.message:null,me=A?.field===`script`||A?.field===`request`?A.message:null,he=()=>{w(!1),j(null),S(!1)},ge=async()=>{if(!ce)return;if(!r){j({field:`request`,message:`No session key available — refresh the page.`});return}S(!0),j(null),w(!1);let e=new AbortController;M.current=setTimeout(()=>{e.abort(),w(!0)},dE);let n={session_key:r,adminLabel:s.replace(/-+$/,``),adminDomain:l,password:_};I&&(n.publicLabel=d.replace(/-+$/,``),n.publicDomain=p),h&&(n.apex=h);try{let r=await fetch(`/api/admin/cloudflare/setup`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(n),signal:e.signal});M.current&&clearTimeout(M.current),M.current=null;let i=await r.json();if(!i.ok){j({field:i.field,message:i.message,output:i.output,correlationId:i.correlationId,streamLogPath:i.streamLogPath});let e=i.streamLogPath?`\n\nstream log: ${i.streamLogPath}`:``;t(i.output&&i.output.length>0?`Cloudflare setup failed: ${i.message}\n\n${i.output}${e}`:`Cloudflare setup failed: ${i.message}${e}`),S(!1);return}if(i.actionId){E(i.actionId);return}t(i.output)}catch(t){if(M.current&&clearTimeout(M.current),M.current=null,e.signal.aborted)return;j({field:`script`,message:t instanceof Error?t.message:`Network error — try again.`}),S(!1)}},_e=(0,B.useMemo)(()=>s?`${s}.${l||`your-domain`}`:`your-name.${l||`your-domain`}`,[s,l]);if(a.status===`loading`)return(0,V.jsx)(O,{submitted:!1,actions:[],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[i?.title&&(0,V.jsx)(`div`,{className:`form-input__title`,children:i.title}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`Reading the domains on your Cloudflare account…`})]})});if(a.status===`empty`)return(0,V.jsx)(O,{submitted:!1,actions:[{label:`Retry`,variant:`primary`,onClick:P}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:`No domains on this Cloudflare account`}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`Add a domain in the Cloudflare dashboard first, then click Retry.`}),(0,V.jsxs)(`ol`,{className:`form-input__field-desc`,style:{margin:0,paddingLeft:20},children:[(0,V.jsxs)(`li`,{children:[`With the correct account active (top-left), click `,(0,V.jsx)(`strong`,{children:`Websites`}),` in the left-hand sidebar.`]}),(0,V.jsxs)(`li`,{children:[`Click the `,(0,V.jsx)(`strong`,{children:`Add a site`}),` button.`]}),(0,V.jsxs)(`li`,{children:[`Enter the bare domain (e.g. `,(0,V.jsx)(`code`,{children:`example.com`}),`) and click `,(0,V.jsx)(`strong`,{children:`Continue`}),`.`]}),(0,V.jsxs)(`li`,{children:[`Review Cloudflare's imported DNS records — preserve website (`,(0,V.jsx)(`code`,{children:`A`}),`), email (`,(0,V.jsx)(`code`,{children:`MX`}),`), and verification (`,(0,V.jsx)(`code`,{children:`TXT`}),`) entries.`]}),(0,V.jsx)(`li`,{children:`Copy Cloudflare's two nameservers and set them on your domain registrar.`}),(0,V.jsxs)(`li`,{children:[`Return to Cloudflare and click `,(0,V.jsx)(`strong`,{children:`Check nameservers`}),`. Wait for the zone to show `,(0,V.jsx)(`strong`,{children:`Active`}),`.`]})]})]})});if(a.status===`error`){let e=a.field===`dashboard`,t=e?`Not signed into Cloudflare on this device`:`Could not read domains from Cloudflare`,n=e?`Sign in again at the Cloudflare dashboard in the VNC browser, then click Retry. If the VNC browser is not open, ask me to run vnc.sh restart.`:a.message;return(0,V.jsx)(O,{submitted:!1,actions:[{label:`Retry`,variant:`primary`,onClick:P}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:t}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:n}),a.output&&a.output.length>0&&(0,V.jsx)(`pre`,{style:{marginTop:8,padding:8,maxHeight:280,overflow:`auto`,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:12,lineHeight:1.4,whiteSpace:`pre-wrap`,wordBreak:`break-word`,background:`var(--surface-2, rgba(0,0,0,0.04))`,borderRadius:4},children:pE(a.output)}),(a.correlationId||a.streamLogPath)&&(0,V.jsxs)(`div`,{style:{marginTop:8,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:11,color:`var(--text-secondary)`,wordBreak:`break-all`},children:[a.correlationId&&(0,V.jsxs)(`div`,{children:[`conversationId: `,a.correlationId]}),a.streamLogPath&&(0,V.jsxs)(`div`,{children:[`stream log: `,a.streamLogPath]})]})]})})}return D?(0,V.jsx)(O,{submitted:!1,actions:[{label:`Try again`,variant:`primary`,onClick:()=>{k(null),E(null),j(null),S(!1),w(!1)}}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:D.card.title}),(0,V.jsx)(`div`,{className:`form-input__desc`,style:{whiteSpace:`pre-wrap`},children:D.card.intro}),(0,V.jsx)(`ol`,{style:{marginTop:10,paddingLeft:20,lineHeight:1.6},children:D.card.steps.map((e,t)=>(0,V.jsx)(`li`,{children:e},t))}),D.card.footnote&&(0,V.jsx)(`div`,{style:{marginTop:10,fontStyle:`italic`,color:`#666`},children:D.card.footnote})]})}):T&&r?(0,V.jsx)(O,{submitted:!1,actions:[],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[i?.title&&(0,V.jsx)(`div`,{className:`form-input__title`,children:i.title}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`Cloudflare setup in progress — keep this panel open until it completes.`}),(0,V.jsx)(`div`,{style:{marginTop:10,height:360},children:(0,V.jsx)(lE,{actionId:T,sessionKey:r,onExit:(e,n)=>{if(N.current.has(T)){console.log(`[CloudflareSetupForm] suppressed duplicate completion dispatch actionId=${T}`);return}if(N.current.add(T),e.code===0){t(`Cloudflare setup completed (actionId: ${T}).`);return}if(n===`authorize-button-not-found`){k({card:uE,actionId:T,exitCode:e.code});return}t(`Cloudflare setup failed with exit code ${e.code} (actionId: ${T}).`)}})})]})}):C?(0,V.jsx)(O,{submitted:!1,actions:[{label:`Reset form`,variant:`ghost`,onClick:he}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[(0,V.jsx)(`div`,{className:`form-input__title`,children:`Setup is taking longer than expected`}),(0,V.jsx)(`div`,{className:`form-input__desc`,children:`The request is still running on the device — check the chat for live progress. Do not resubmit. If the script fails, the error will appear in chat and you can retry from there.`})]})}):(0,V.jsx)(O,{submitted:n,submittedLabel:`Submitting…`,actions:[{label:x?`Setting up…`:i?.submitLabel??`Set up Cloudflare`,variant:`primary`,disabled:!ce,onClick:ge}],children:(0,V.jsxs)(`div`,{className:`form-input`,children:[i?.title&&(0,V.jsx)(`div`,{className:`form-input__title`,children:i.title}),i?.description&&(0,V.jsx)(`div`,{className:`form-input__desc`,children:i.description}),(0,V.jsxs)(`div`,{className:`form-input__fields`,children:[(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-admin-label`,children:[`Admin address`,(0,V.jsx)(`span`,{className:`form-input__required`,children:`*`})]}),(0,V.jsxs)(`div`,{className:`tunnel-route__row`,children:[(0,V.jsx)(`input`,{id:`cf-admin-label`,className:`form-input__input tunnel-route__input`,type:`text`,autoCapitalize:`none`,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,placeholder:`your-name`,value:s,onChange:e=>te(e.target.value),disabled:n||x}),(0,V.jsx)(hE,{id:`cf-admin-domain`,value:l,options:ee,onChange:u,disabled:n||x})]}),(0,V.jsx)(`div`,{className:`form-input__field-desc`,children:`Where you access your platform remotely, via your main domain.`}),(0,V.jsxs)(`div`,{className:`form-input__field-desc`,children:[`Preview: `,(0,V.jsx)(`span`,{className:`tunnel-route__preview`,children:_e})]}),(de||le)&&(0,V.jsx)(`div`,{className:`tunnel-route__error`,children:de??le})]}),(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-public-label`,children:[`Public address`,(0,V.jsx)(`span`,{className:`tunnel-route__optional`,children:`Optional`})]}),(0,V.jsxs)(`div`,{className:`tunnel-route__row`,children:[(0,V.jsx)(`input`,{id:`cf-public-label`,className:`form-input__input tunnel-route__input`,type:`text`,autoCapitalize:`none`,autoComplete:`off`,autoCorrect:`off`,spellCheck:!1,placeholder:`leave empty to skip`,value:d,onChange:e=>ne(e.target.value),disabled:n||x}),(0,V.jsx)(hE,{id:`cf-public-domain`,value:p,options:ee,onChange:m,disabled:n||x||!I})]}),(0,V.jsx)(`div`,{className:`form-input__field-desc`,children:`Where visitors engage with your public agents, via your main domain. Leave empty for admin-only access.`}),(fe||ue)&&(0,V.jsx)(`div`,{className:`tunnel-route__error`,children:fe??ue})]}),(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-apex`,children:[`Proxy apex`,(0,V.jsx)(`span`,{className:`tunnel-route__optional`,children:`Optional`})]}),(0,V.jsxs)(`select`,{id:`cf-apex`,className:`form-input__input`,value:h,onChange:e=>g(e.target.value),disabled:n||x,children:[(0,V.jsx)(`option`,{value:``,children:`None`}),ee.map(e=>(0,V.jsx)(`option`,{value:e,children:e},e))]}),(0,V.jsx)(`div`,{className:`form-input__field-desc`,children:`Alternative domain for public agent visitors.`})]}),(0,V.jsxs)(`div`,{className:`form-input__field`,children:[(0,V.jsxs)(`label`,{className:`form-input__label`,htmlFor:`cf-password`,children:[`Admin password`,(0,V.jsx)(`span`,{className:`form-input__required`,children:`*`})]}),(0,V.jsxs)(`div`,{className:`tunnel-route__row`,children:[(0,V.jsx)(`input`,{id:`cf-password`,className:`form-input__input tunnel-route__input`,type:y?`text`:`password`,autoCapitalize:`none`,autoComplete:`new-password`,autoCorrect:`off`,spellCheck:!1,value:_,onChange:e=>{v(e.target.value),j(null)},disabled:n||x}),(0,V.jsx)(`button`,{type:`button`,className:`tunnel-route__suffix`,onClick:()=>b(e=>!e),disabled:n||x,style:{cursor:`pointer`,border:`none`,background:`inherit`},"aria-label":y?`Hide password`:`Show password`,children:y?`Hide`:`Show`})]}),(0,V.jsx)(`ul`,{className:`form-input__field-desc`,style:{margin:0,paddingLeft:16},children:se.map(e=>(0,V.jsxs)(`li`,{style:{color:e.met?`var(--text)`:`var(--text-secondary)`},children:[e.met?`✓`:`○`,` `,e.label]},e.key))}),pe&&(0,V.jsx)(`div`,{className:`tunnel-route__error`,children:pe})]}),me&&(0,V.jsxs)(`div`,{className:`tunnel-route__error`,role:`alert`,children:[(0,V.jsx)(`div`,{children:me}),A?.output&&A.output.length>0&&(0,V.jsx)(`pre`,{style:{marginTop:8,padding:8,maxHeight:280,overflow:`auto`,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:12,lineHeight:1.4,whiteSpace:`pre-wrap`,wordBreak:`break-word`,background:`var(--surface-2, rgba(0,0,0,0.04))`,borderRadius:4},children:pE(A.output)}),(A?.correlationId||A?.streamLogPath)&&(0,V.jsxs)(`div`,{style:{marginTop:8,fontFamily:`var(--font-mono, ui-monospace, monospace)`,fontSize:11,color:`var(--text-secondary)`,wordBreak:`break-all`},children:[A.correlationId&&(0,V.jsxs)(`div`,{children:[`conversationId: `,A.correlationId]}),A.streamLogPath&&(0,V.jsxs)(`div`,{children:[`stream log: `,A.streamLogPath]})]})]})]})]})})}function hE({id:e,value:t,options:n,onChange:r,disabled:i}){return(0,V.jsx)(`select`,{id:e,className:`tunnel-route__suffix`,value:t,onChange:e=>r(e.target.value),disabled:i,style:{cursor:i?`default`:`pointer`,border:`none`},children:n.map(e=>(0,V.jsxs)(`option`,{value:e,children:[`.`,e]},e))})}var gE={"single-select":b,"multi-select":P,confirm:Gt,"info-card":Kt,"action-list":qt,form:Jt,progress:Xt,"browser-viewer":Qt,"rich-content-editor":uT,"grid-editor":_T,"qr-code":GT,"file-attachment":JT,"action-buttons":j,"cloudflare-setup-form":mE,"output-style":b,"thinking-view":b,"plugin-selector":P};function _E(e){let{data:t,filePath:n,onOpen:r}=e,i=n.split(`/`).pop()||n||`document.md`,a=(i.split(`.`).pop()||`doc`).toUpperCase().slice(0,6),o=n||i,s=(0,B.useRef)(!1);return(0,B.useEffect)(()=>{s.current||(s.current=!0,r({docId:o,name:i,content:t.content??``}))},[o,i,t.content,r]),(0,V.jsx)(`div`,{className:`component-card artefact-ref-card`,children:(0,V.jsxs)(`button`,{type:`button`,className:`artefact-ref-button`,onClick:()=>r({docId:o,name:i,content:t.content??``}),children:[(0,V.jsx)(`span`,{className:`artefact-ref-icon`,children:(0,V.jsx)(c,{size:16})}),(0,V.jsxs)(`span`,{className:`artefact-ref-meta`,children:[(0,V.jsx)(`span`,{className:`artefact-ref-name`,children:i}),(0,V.jsxs)(`span`,{className:`artefact-ref-tag`,children:[a,` · open in artefact pane`]})]}),(0,V.jsx)(`span`,{className:`artefact-ref-open`,children:(0,V.jsx)(Fe,{size:14})})]})})}function vE({name:e,data:t,onSubmit:n,submitted:r,isStreaming:i,sessionKey:a,onOpenArtefact:o}){if(e===`document-editor`&&o){let e=t??{};return(0,V.jsx)(_E,{data:e,filePath:e.filePath||e.title||`document.md`,onOpen:o})}if(e===`document-editor`)return(0,V.jsx)(Jw,{data:t,onSubmit:n,submitted:r,isStreaming:i});let s=gE[e];return s?(0,V.jsx)(s,{data:t,onSubmit:n,submitted:r,isStreaming:i,sessionKey:a}):(console.warn(`[ComponentRenderer] Unknown component: "${e}". Registered: ${Object.keys(gE).join(`, `)}`),(0,V.jsx)(`div`,{className:`component-card component-card--error`,children:(0,V.jsxs)(`p`,{style:{fontFamily:`var(--font-body)`,fontSize:12,color:`var(--text-secondary)`},children:[`Component “`,e,`” is not available. This may require a platform update.`]})}))}var yE=/\b(?:fail|error|unable|could not|cannot|CAPTCHA|SIGN_IN_REQUIRED)\b/i;function bE(e){let t=new Date(e),n=new Date,r=String(t.getHours()).padStart(2,`0`),i=String(t.getMinutes()).padStart(2,`0`);return t.toDateString()===n.toDateString()?`${r}:${i}`:`${String(t.getDate()).padStart(2,`0`)}-${String(t.getMonth()+1).padStart(2,`0`)}-${t.getFullYear()} ${r}:${i}`}var xE=[pt,ut,be,ct,Ne];function SE({size:e=13}){let[t,n]=(0,B.useState)(0);(0,B.useEffect)(()=>{let e=setInterval(()=>n(e=>(e+1)%8),300);return()=>clearInterval(e)},[]);let r=t<=4?t:8-t,i=xE[r];return(0,V.jsx)(i,{size:e,className:`star-loader star-frame-${r}`})}var CE=[`Thinking`,`Reasoning`,`Analyzing`,`Considering`,`Reflecting`,`Evaluating`,`Weighing`,`Synthesizing`,`Processing`,`Exploring`,`Examining`,`Assessing`,`Reviewing`,`Contemplating`,`Deliberating`],wE=60,TE=1500;function EE(){let e=(0,B.useRef)(-1),t=(0,B.useRef)(void 0),[n,r]=(0,B.useState)(()=>{let t=Math.floor(Math.random()*CE.length);return e.current=t,CE[t]+`…`});return(0,B.useEffect)(()=>{let n=!1;function i(){let t;do t=Math.floor(Math.random()*CE.length);while(t===e.current&&CE.length>1);return e.current=t,t}function a(){if(n)return;let e=CE[i()]+`…`,o=0;function s(){n||(o++,r(e.slice(0,o)),o<e.length?t.current=setTimeout(s,wE):t.current=setTimeout(a,TE))}t.current=setTimeout(s,wE)}return t.current=setTimeout(a,TE),()=>{n=!0,clearTimeout(t.current)}},[]),(0,V.jsx)(`span`,{className:`tl-thinking-typewriter`,children:n})}function DE(e,t){let n=e=>String(e??``);switch(e){case`Read`:return`Read ${n(t.file_path??t.path)}`;case`Write`:return`Write ${n(t.file_path??t.path)}`;case`Edit`:return`Edit ${n(t.file_path??t.path)}`;case`Bash`:return n(t.description??String(t.command??``).slice(0,70));case`Glob`:return`Glob ${n(t.pattern)}`;case`Grep`:return`Grep "${n(t.pattern)}" in ${n(t.path??`.`)}`;case`WebFetch`:return`Fetch ${n(t.url)}`;case`Agent`:return n(t.description??t.task??`Subagent`);case`Skill`:return n(t.skill??`Skill`);default:{let r=e.match(/^mcp__[^_]+__(.+)$/),i=r?r[1]:e,a=Object.keys(t)[0];return a?`${i}: ${n(t[a]).slice(0,50)}`:i}}}function OE({name:e,size:t=12}){let n=e.match(/^mcp__[^_]+__(.+)$/);switch(n?n[1]:e){case`Read`:return(0,V.jsx)(c,{size:t});case`Write`:return(0,V.jsx)(Ve,{size:t});case`Edit`:return(0,V.jsx)(Be,{size:t});case`Bash`:return(0,V.jsx)(mt,{size:t});case`Glob`:case`Grep`:return(0,V.jsx)(de,{size:t});case`WebFetch`:return(0,V.jsx)(D,{size:t});case`Agent`:return(0,V.jsx)(Ce,{size:t});case`Skill`:return(0,V.jsx)(ut,{size:t});case`memory-search`:return(0,V.jsx)(Me,{size:t});case`memory-write`:return(0,V.jsx)(je,{size:t});case`task-list`:return(0,V.jsx)(R,{size:t});case`task-create`:return(0,V.jsx)(ke,{size:t});case`contact-create`:return(0,V.jsx)(ht,{size:t});default:return(0,V.jsx)(_t,{size:t})}}function kE(e){let t=new Map,n=new Map;return e.forEach((e,r)=>{if(e.type===`tool_use`){let t=n.get(e.name)??[];t.push(r),n.set(e.name,t)}else if(e.type===`tool_result`){let i=n.get(e.name);i?.length&&t.set(i.shift(),r)}}),t}function AE(e){return e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function jE(e){let t=``;switch(e.type){case`text`:case`thinking`:t=e.content??``;break;case`tool_use`:t=JSON.stringify(e.input??{});break;case`tool_result`:t=e.output??``;break;case`subagent_start`:t=e.task??``;break;case`subagent_end`:t=e.result??``;break;case`component`:t=JSON.stringify(e.data??{});break;case`status`:t=e.message??``;break;default:return 0}return Math.ceil(t.length/4)}function ME(e){if(e<60)return`${e}s`;let t=Math.floor(e/60),n=e%60;return n>0?`${t}m ${n}s`:`${t}m`}function NE(e){if(e<60)return`${e.toFixed(1)}s`;let t=Math.floor(e/60),n=e%60;return n>=.1?`${t}m ${Math.floor(n)}s`:`${t}m`}function PE({icon:e,isPending:t,isError:n,doneClass:r=`tl-done`,summary:i,detail:a,tokens:o,elapsed:s,isLast:c,expanded:l,onToggle:u,selectCheck:d}){let f=!!a;return(0,V.jsxs)(`div`,{className:`tl-step`,children:[d,(0,V.jsxs)(`div`,{className:`tl-col`,children:[(0,V.jsx)(`div`,{className:`tl-icon${t?` tl-pending`:n?` tl-error`:` ${r}`}`,children:e}),!c&&(0,V.jsx)(`div`,{className:`tl-line tl-line-grow`})]}),(0,V.jsxs)(`div`,{className:`tl-body`,children:[(0,V.jsxs)(`div`,{className:`tl-row`,onClick:f?()=>{window.getSelection()?.toString()||u()}:void 0,style:{cursor:f?`pointer`:`default`},children:[(0,V.jsx)(`span`,{className:`tl-summary`,children:i}),o>0&&(0,V.jsxs)(`span`,{className:`tl-step-tokens`,children:[(0,V.jsx)(vt,{size:9}),AE(o)]}),(0,V.jsx)(`span`,{className:`tl-step-elapsed`,children:NE(s)}),f&&(0,V.jsx)(`span`,{className:`tl-chevron`,children:l?(0,V.jsx)(p,{size:10}):(0,V.jsx)(Ee,{size:10})})]}),f&&l&&(0,V.jsx)(`pre`,{className:`tl-detail`,children:a})]})]})}function FE({events:e,isStreaming:t,elapsedSeconds:n,streamStartMs:r,expandAll:i,onCompactNow:a,isCompacting:o,sessionCompacted:s,onComponentSubmit:c,submittedComponents:l,timestamp:u,selectionMode:d,selectedItems:f,onToggleItem:m,sessionKey:g,onOpenArtefact:_}){let[v,y]=(0,B.useState)(new Set),[b,S]=(0,B.useState)(!t),C=(0,B.useRef)(t);(0,B.useEffect)(()=>{C.current&&!t&&S(!0),!C.current&&t&&S(!1),C.current=t},[t]);let T=(0,B.useRef)(new Map),E=(0,B.useRef)(0),[D,O]=(0,B.useState)(Date.now());(0,B.useEffect)(()=>{let t=Date.now();for(let n=E.current;n<e.length;n++)T.current.has(n)||T.current.set(n,t);E.current=e.length},[e]),(0,B.useEffect)(()=>{if(!t)return;let e=setInterval(()=>O(Date.now()),100);return()=>clearInterval(e)},[t]);let k=e=>{y(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},A=(e,t)=>{let n=e===ae[0]?.i&&r?r:T.current.get(e)??D,i=t===void 0?D:T.current.get(t)??D;return Math.max(0,(i-n)/1e3)},j=kE(e),M=new Set(j.values()),N=e.map((e,t)=>({e,i:t})).filter(({e})=>e.type===`text`),P=new Set(t?[]:e.map((e,t)=>({e,i:t})).filter(({e})=>e.type===`component`).map(({i:e})=>e)),ee=e=>e.type===`component`&&[`browser-viewer`,`qr-code`].includes(e.name);function ne(e){if(!d||t||!u)return null;let n=`${u}_${e}`;return(0,V.jsx)(`div`,{className:`tl-select-check`,onClick:e=>e.stopPropagation(),children:(0,V.jsx)(_e,{checked:f?.has(n)??!1,onChange:()=>m?.(n)})})}let F=e.findLast(e=>e.type===`usage`),I=e.findLast(e=>e.type===`history_usage`),re=e.findLast(e=>e.type===`status`),ie=e.some(e=>e.type===`subagent_start`),ae=e.map((e,t)=>({e,i:t})).filter(({e,i:n})=>e.type!==`text`&&e.type!==`usage`&&e.type!==`rate_limit`&&e.type!==`history_usage`&&e.type!==`status`&&e.type!==`done`&&e.type!==`subagent_progress`&&!M.has(n)&&!P.has(n)&&!(t&&e.type===`component`&&!ee(e))&&!(e.type===`tool_use`&&e.name===`Agent`&&ie)),se=new Map;for(let e=0;e<ae.length-1;e++)se.set(ae[e].i,ae[e+1].i);let L=(()=>{let e=[],t=!1,n=0;for(let{e:r}of ae)if(n+=jE(r),r.type===`thinking`)t=!0;else if(r.type===`tool_use`){let t=r.name;e.includes(t)||e.push(t)}let r=[`${ae.length} step${ae.length===1?``:`s`}`];if(e.length>0){let t=e.slice(0,3),n=e.length-t.length;r.push(t.join(`, `)+(n>0?` +${n}`:``))}return{text:r.join(` · `),hasThinking:t,tokenSum:n}})(),ce=F?.peak_request_pct==null?F?.context_window?Math.round((F.input_tokens+F.cache_creation_tokens+F.cache_read_tokens)/F.context_window*100):null:Math.round(F.peak_request_pct*100),le=ae.length>0,ue=N.length>0,de=new Set(ae.map(e=>e.i)),fe=new Set(N.map(e=>e.i)),pe=[];for(let t=0;t<e.length;t++){let n=e[t],r=null;if(de.has(t)?r=`steps`:fe.has(t)&&(r=`text`),!r)continue;let i=pe[pe.length-1];i&&i.role===r?i.items.push({e:n,i:t}):pe.push({role:r,items:[{e:n,i:t}]})}let me=N.length>0?N[N.length-1].i:-1;function he(n,r,a,o){let s=o===a.length-1;if(n.type===`thinking`){let e=i===void 0?!v.has(r):i,t=se.get(r);return(0,V.jsxs)(`div`,{className:`tl-step`,children:[ne(r),(0,V.jsxs)(`div`,{className:`tl-col`,children:[(0,V.jsx)(`div`,{className:`tl-icon tl-dim`,children:(0,V.jsx)(qe,{size:11})}),!s&&(0,V.jsx)(`div`,{className:`tl-line tl-line-grow`})]}),(0,V.jsx)(`div`,{className:`tl-body`,children:(0,V.jsxs)(`div`,{className:`tl-row tl-row-top`,onClick:()=>{window.getSelection()?.toString()||k(r)},style:{cursor:`pointer`},children:[(0,V.jsx)(`div`,{className:`tl-thinking-col`,children:e?(0,V.jsx)(`div`,{className:`tl-thinking-body`,children:n.content}):(0,V.jsxs)(`span`,{className:`tl-summary tl-thinking-label`,children:[n.content.slice(0,80),n.content.length>80?`…`:``]})}),jE(n)>0&&(0,V.jsxs)(`span`,{className:`tl-step-tokens`,children:[(0,V.jsx)(vt,{size:9}),AE(jE(n))]}),(0,V.jsx)(`span`,{className:`tl-step-elapsed`,children:NE(A(r,t))}),(0,V.jsx)(`span`,{className:`tl-chevron`,children:e?(0,V.jsx)(p,{size:10}):(0,V.jsx)(Ee,{size:10})})]})})]},r)}if(n.type===`status`)return(0,V.jsx)(`div`,{className:`tl-status`,children:n.message},r);if(n.type===`tool_use`){let t=j.get(r),a=t===void 0?void 0:e[t],o=a===void 0,c=a?.type===`tool_result`&&!!a.error,l=o?(0,V.jsx)(Ze,{size:11,className:`tl-spinner`}):c?(0,V.jsx)(oe,{size:11}):(0,V.jsx)(OE,{name:n.name,size:11}),u=n,d=a?.type===`tool_result`?`Input:
344
344
  ${JSON.stringify(u.input,null,2)}
345
345
 
346
346
  Result:
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Maxy</title>
7
7
  <link rel="icon" href="/favicon.ico">
8
- <script type="module" crossorigin src="/assets/admin-Sa301b8q.js"></script>
8
+ <script type="module" crossorigin src="/assets/admin-B41Gr-FL.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-DD-I1_y5.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-DJER3a7U.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-qlgyTAkD.js">
@@ -1,74 +0,0 @@
1
- // Tri-state DOM matcher for the Cloudflare argotunnel consent page (Task 855).
2
- //
3
- // `dash.cloudflare.com/argotunnel?...` legitimately renders three observable
4
- // states for our flow:
5
- //
6
- // 1. Pre-authorize — a <button> or <input type="submit"> whose trimmed text
7
- // matches /^(authorize|connect)$/i (and is not disabled). Click it; the
8
- // callback fires, cloudflared writes ~/.cloudflared/cert.pem.
9
- //
10
- // 2. Post-success — the dashboard renders a Success modal containing the
11
- // stable substring "Cloudflared has installed a certificate". This shape
12
- // is reached when the user already authorised this account before (or
13
- // did so manually in VNC between cloudflared spawn and helper poll).
14
- // The OAuth callback is idempotent on the cert side: when the helper
15
- // sees this state, the running cloudflared subprocess will still write
16
- // ~/.cloudflared/cert.pem because the callback URL is exercised by the
17
- // navigation. The wrapper drops into the existing cert-poll afterwards.
18
- //
19
- // 3. Neither — the page is mid-load, blank, or in some other state. The
20
- // caller polls again until BUTTON_POLL_TIMEOUT_MS, then exits 1.
21
- //
22
- // The matcher returns a discriminated union from a single DOM read:
23
- //
24
- // { kind: 'button', descriptor: {tag,text,disabled} } | { kind: 'success' } | null
25
- //
26
- // PRIORITY: button > success modal. Page transitions can briefly render both
27
- // (e.g. during the success animation). If a clickable Authorize button exists,
28
- // click it — the click produces a fresh callback regardless of any leftover
29
- // modal text. Only when no button is present do we trust the modal as the
30
- // terminal state.
31
- //
32
- // EXPORTED SHAPES:
33
- //
34
- // * findMatch(doc) — JS function form. Used by JSDOM-based unit tests
35
- // (platform/ui/scripts/__tests__/cdp-authorize-matcher.test.ts) so future
36
- // matcher edits replay against captured DOM fixtures offline (Success
37
- // criterion 8 of Task 855).
38
- //
39
- // * MATCH_EXPR — string form, evaluated by Chrome DevTools Protocol's
40
- // Runtime.evaluate in _cdp-authorize.mjs's polling loop. Built from
41
- // findMatch.toString() so the live page and the tests run identical
42
- // logic — single source of truth.
43
- //
44
- // CONTRACT — DO NOT loosen the success-modal anchor. Tighter anchors regress
45
- // on Cloudflare copy edits; "Cloudflared has installed a certificate" is the
46
- // load-bearing claim of the modal. Wider anchors (e.g. matching just
47
- // "certificate") would false-positive on the pre-authorize page.
48
-
49
- export function findMatch(doc) {
50
- const candidates = Array.from(doc.querySelectorAll('button, input[type="submit"]'));
51
- const match = candidates.find((el) => {
52
- const text = (el.textContent ?? el.value ?? '').trim();
53
- return /^(authorize|connect)$/i.test(text) && !el.disabled;
54
- });
55
- if (match) {
56
- const descriptor = {
57
- tag: match.tagName.toLowerCase(),
58
- text: (match.textContent ?? match.value ?? '').trim().slice(0, 40),
59
- disabled: Boolean(match.disabled),
60
- };
61
- match.click();
62
- return { kind: 'button', descriptor };
63
- }
64
- // textContent over innerText: jsdom's innerText is partial; the dashboard's
65
- // success-modal text is plain DOM content (not visibility-gated by CSS for
66
- // anyone reading the page) so textContent finds it on both runtimes.
67
- const bodyText = doc.body ? doc.body.textContent || '' : '';
68
- if (bodyText.includes('Cloudflared has installed a certificate')) {
69
- return { kind: 'success' };
70
- }
71
- return null;
72
- }
73
-
74
- export const MATCH_EXPR = `(${findMatch.toString()})(document)`;
@@ -1,284 +0,0 @@
1
- #!/usr/bin/env node
2
- // Drive the Cloudflare argotunnel consent page to a deterministic terminal
3
- // state via CDP WebSocket. Originally Task 588 (Authorize click from code,
4
- // collapsing a 180 s human-latency window). Task 855 widens the contract to
5
- // three states because the dashboard does not always render a button — once
6
- // the bound account has authorized this device's zones, navigating the same
7
- // (or a fresh) consent URL renders a Success modal verbatim, and the
8
- // pre-855 helper conflated "no button visible yet" with "no button will
9
- // ever appear" and exited error.
10
- //
11
- // Protocol: Chrome DevTools Protocol over WebSocket
12
- // (https://chromedevtools.github.io/devtools-protocol/):
13
- // 1. GET http://127.0.0.1:9222/json/list → find the target with matching id;
14
- // extract webSocketDebuggerUrl.
15
- // 2. Open WS, enable Page + Runtime domains.
16
- // 3. Wait for Page.loadEventFired (or observe document.readyState==='complete').
17
- // 4. Poll Runtime.evaluate every 200 ms for up to ~3 s, evaluating the
18
- // tri-state matcher from _cdp-authorize-matcher.mjs. The matcher
19
- // returns one of:
20
- // - { kind:'button', descriptor } — Authorize/Connect button found,
21
- // click() fired in-page; emit
22
- // reason=clicked, exit 0.
23
- // - { kind:'success' } — Success modal text detected
24
- // ("Cloudflared has installed a
25
- // certificate"); cert is bound to
26
- // the account. Emit reason=
27
- // cert-already-installed, exit 0.
28
- // Caller's existing cert-poll picks
29
- // up the cert (callback is
30
- // idempotent on this navigation).
31
- // - null — Neither anchor matched yet; loop.
32
- // After BUTTON_POLL_TIMEOUT_MS with no terminal match, emit
33
- // reason=authorize-button-not-found, exit 1.
34
- //
35
- // Exit codes:
36
- // 0 - terminal success (reason=clicked OR reason=cert-already-installed)
37
- // 1 - authorize-button-not-found (loud failure — button AND modal absent)
38
- // 2 - cdp-ws-unreachable or node-websocket-unavailable
39
- // 3 - target-not-found (the target_id isn't in /json/list)
40
- // 4 - click-evaluate-threw (Runtime.evaluate returned wasThrown=true)
41
- // 5 - protocol-error (malformed CDP response)
42
- //
43
- // Stdout contract (read by setup-tunnel.sh's awk-based reason parser):
44
- // cdp-authorize result=<ok|error> reason=<…> elapsed_ms=<…> [detail=<…>]
45
- // Exactly ONE such line per invocation; the wrapper takes the LAST
46
- // `result=ok` line via awk so future debug-line additions cannot mis-route.
47
-
48
- import { MATCH_EXPR } from './_cdp-authorize-matcher.mjs';
49
-
50
- const CDP_HOST = '127.0.0.1';
51
- const CDP_PORT = 9222;
52
- const HTTP_TIMEOUT_MS = 3000;
53
- const BUTTON_POLL_TIMEOUT_MS = 3000;
54
- const BUTTON_POLL_INTERVAL_MS = 200;
55
- const LOAD_WAIT_TIMEOUT_MS = 5000;
56
- const WS_CONNECT_TIMEOUT_MS = 3000;
57
- const RESULT_WAIT_TIMEOUT_MS = 3000;
58
-
59
- function emit(result, reason, extra = {}) {
60
- const parts = [`cdp-authorize result=${result} reason=${reason}`];
61
- for (const [k, v] of Object.entries(extra)) {
62
- if (v === undefined || v === null) continue;
63
- const s = String(v).replace(/\s+/g, ' ');
64
- parts.push(`${k}="${s.slice(0, 200)}"`);
65
- }
66
- process.stdout.write(parts.join(' ') + '\n');
67
- }
68
-
69
- function die(code, reason, extra) {
70
- emit('error', reason, extra);
71
- process.exit(code);
72
- }
73
-
74
- if (typeof WebSocket === 'undefined') {
75
- die(2, 'node-websocket-unavailable', {
76
- detail: `Node ${process.version} has no global WebSocket — upgrade to Node 22+`,
77
- });
78
- }
79
-
80
- const targetId = process.argv[2];
81
- if (!targetId) {
82
- die(2, 'missing-target-id', { detail: 'usage: _cdp-authorize.mjs <target-id>' });
83
- }
84
-
85
- const started = Date.now();
86
-
87
- async function fetchTargets() {
88
- const ac = new AbortController();
89
- const timer = setTimeout(() => ac.abort(), HTTP_TIMEOUT_MS);
90
- try {
91
- const res = await fetch(`http://${CDP_HOST}:${CDP_PORT}/json/list`, { signal: ac.signal });
92
- if (!res.ok) throw new Error(`/json/list returned ${res.status}`);
93
- return await res.json();
94
- } finally {
95
- clearTimeout(timer);
96
- }
97
- }
98
-
99
- let targets;
100
- try {
101
- targets = await fetchTargets();
102
- } catch (err) {
103
- die(2, 'cdp-ws-unreachable', { detail: err instanceof Error ? err.message : String(err) });
104
- }
105
-
106
- const target = Array.isArray(targets) ? targets.find((t) => t && t.id === targetId) : undefined;
107
- if (!target || typeof target.webSocketDebuggerUrl !== 'string') {
108
- die(3, 'target-not-found', { target_id: targetId, count: Array.isArray(targets) ? targets.length : -1 });
109
- }
110
-
111
- // Open WebSocket. Native Node 22 global.
112
- let ws;
113
- try {
114
- ws = new WebSocket(target.webSocketDebuggerUrl);
115
- } catch (err) {
116
- die(2, 'cdp-ws-unreachable', { detail: err instanceof Error ? err.message : String(err) });
117
- }
118
-
119
- // WS open with race guards. Three outcomes are possible:
120
- // 1. `open` fires before `error` / timeout → resolve and continue.
121
- // 2. `error` fires before `open` → reject; caller emits die() and exits.
122
- // 3. Timeout fires before either → reject; caller emits die() and exits.
123
- // The `.once` on `open`/`error` listeners handles the race where both fire
124
- // in quick succession (some proxy-protocol mismatches can emit `error`
125
- // before the open event is dispatched). The catch below awaits the die()
126
- // call and then `throw`s to guarantee execution does not fall through to
127
- // the Page.enable call with a dead socket — even if a future refactor
128
- // removes process.exit() from die().
129
- try {
130
- await new Promise((resolveOpen, rejectOpen) => {
131
- const timer = setTimeout(() => rejectOpen(new Error('ws open timeout')), WS_CONNECT_TIMEOUT_MS);
132
- ws.addEventListener('open', () => { clearTimeout(timer); resolveOpen(); }, { once: true });
133
- ws.addEventListener('error', (e) => { clearTimeout(timer); rejectOpen(new Error(`ws error: ${e.message ?? 'unknown'}`)); }, { once: true });
134
- });
135
- } catch (err) {
136
- die(2, 'cdp-ws-unreachable', { detail: err instanceof Error ? err.message : String(err) });
137
- throw err; // unreachable — die() exits, but guards the type system and future refactors.
138
- }
139
-
140
- // Message router: CDP replies come back keyed by the `id` we sent. Events
141
- // arrive with no `id` but a `method` (e.g. "Page.loadEventFired").
142
- let nextId = 1;
143
- const pending = new Map();
144
- const eventListeners = new Map();
145
-
146
- ws.addEventListener('message', (evt) => {
147
- let msg;
148
- try {
149
- msg = JSON.parse(typeof evt.data === 'string' ? evt.data : evt.data.toString());
150
- } catch {
151
- return;
152
- }
153
- if (typeof msg.id === 'number' && pending.has(msg.id)) {
154
- const { resolveIt, rejectIt } = pending.get(msg.id);
155
- pending.delete(msg.id);
156
- if (msg.error) rejectIt(new Error(`CDP error: ${msg.error.message ?? JSON.stringify(msg.error)}`));
157
- else resolveIt(msg.result);
158
- } else if (typeof msg.method === 'string') {
159
- const listeners = eventListeners.get(msg.method);
160
- if (listeners) for (const l of listeners) l(msg.params);
161
- }
162
- });
163
-
164
- ws.addEventListener('close', () => {
165
- for (const { rejectIt } of pending.values()) {
166
- rejectIt(new Error('ws closed while awaiting response'));
167
- }
168
- pending.clear();
169
- });
170
-
171
- function send(method, params = {}) {
172
- const id = nextId++;
173
- return new Promise((resolveIt, rejectIt) => {
174
- const timer = setTimeout(() => {
175
- pending.delete(id);
176
- rejectIt(new Error(`CDP ${method} timed out after ${RESULT_WAIT_TIMEOUT_MS}ms`));
177
- }, RESULT_WAIT_TIMEOUT_MS);
178
- pending.set(id, {
179
- resolveIt: (r) => { clearTimeout(timer); resolveIt(r); },
180
- rejectIt: (e) => { clearTimeout(timer); rejectIt(e); },
181
- });
182
- ws.send(JSON.stringify({ id, method, params }));
183
- });
184
- }
185
-
186
- function onEvent(method, listener) {
187
- let set = eventListeners.get(method);
188
- if (!set) { set = new Set(); eventListeners.set(method, set); }
189
- set.add(listener);
190
- return () => set.delete(listener);
191
- }
192
-
193
- // Enable the two domains we need. Page.enable fires events; Runtime.enable
194
- // lets us run expressions in the page context.
195
- try {
196
- await send('Page.enable');
197
- await send('Runtime.enable');
198
- } catch (err) {
199
- die(5, 'protocol-error', { detail: err.message });
200
- }
201
-
202
- // Wait for load — if the page is already loaded (navigated before this helper
203
- // ran), Page.loadEventFired may never fire again; poll document.readyState
204
- // as a fallback. The first of the two to resolve wins.
205
- async function waitForLoad() {
206
- const loadFired = new Promise((resolveIt) => {
207
- const off = onEvent('Page.loadEventFired', () => { off(); resolveIt('loadEvent'); });
208
- });
209
- const readyStatePoll = (async () => {
210
- const deadline = Date.now() + LOAD_WAIT_TIMEOUT_MS;
211
- while (Date.now() < deadline) {
212
- try {
213
- const r = await send('Runtime.evaluate', {
214
- expression: 'document.readyState',
215
- returnByValue: true,
216
- });
217
- if (r?.result?.value === 'complete' || r?.result?.value === 'interactive') return 'readyState';
218
- } catch { /* keep polling */ }
219
- await new Promise((res) => setTimeout(res, 200));
220
- }
221
- throw new Error('load-wait-timeout');
222
- })();
223
- return Promise.race([loadFired, readyStatePoll]);
224
- }
225
-
226
- try {
227
- await waitForLoad();
228
- } catch (err) {
229
- die(1, 'page-load-timeout', { detail: err.message, elapsed_ms: Date.now() - started });
230
- }
231
-
232
- // Poll the consent page until one of the three terminal states resolves.
233
- // MATCH_EXPR is built from _cdp-authorize-matcher.mjs's findMatch so the
234
- // JSDOM unit tests and the live CDP path execute literally identical logic.
235
- const matchDeadline = Date.now() + BUTTON_POLL_TIMEOUT_MS;
236
- let matched = null;
237
- while (Date.now() < matchDeadline) {
238
- let r;
239
- try {
240
- r = await send('Runtime.evaluate', {
241
- expression: MATCH_EXPR,
242
- returnByValue: true,
243
- awaitPromise: false,
244
- });
245
- } catch (err) {
246
- die(5, 'protocol-error', { detail: err.message });
247
- }
248
- if (r?.exceptionDetails) {
249
- die(4, 'click-evaluate-threw', {
250
- detail: r.exceptionDetails.text ?? 'unknown exception',
251
- elapsed_ms: Date.now() - started,
252
- });
253
- }
254
- const val = r?.result?.value;
255
- if (val && typeof val === 'object' && (val.kind === 'button' || val.kind === 'success')) {
256
- matched = val;
257
- break;
258
- }
259
- await new Promise((res) => setTimeout(res, BUTTON_POLL_INTERVAL_MS));
260
- }
261
-
262
- if (!matched) {
263
- die(1, 'authorize-button-not-found', { elapsed_ms: Date.now() - started });
264
- }
265
-
266
- if (matched.kind === 'button') {
267
- const d = matched.descriptor ?? {};
268
- emit('ok', 'clicked', {
269
- tag: d.tag,
270
- text: d.text,
271
- elapsed_ms: Date.now() - started,
272
- });
273
- } else {
274
- // matched.kind === 'success' — Success modal text is on the page; the
275
- // bound account already has a cert. Caller drops into the cert-poll loop;
276
- // the in-flight cloudflared subprocess receives the idempotent callback
277
- // and writes ~/.cloudflared/cert.pem.
278
- emit('ok', 'cert-already-installed', {
279
- elapsed_ms: Date.now() - started,
280
- });
281
- }
282
-
283
- try { ws.close(); } catch { /* best-effort */ }
284
- process.exit(0);