@rubytech/create-maxy 1.0.627 → 1.0.629
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/platform/plugins/cloudflare/scripts/_stream-log.sh +124 -0
- package/payload/platform/plugins/cloudflare/scripts/reset-tunnel.sh +45 -3
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +151 -10
- package/payload/platform/plugins/docs/references/plugins-guide.md +2 -0
- package/payload/server/public/assets/admin-CGIu9HnV.js +352 -0
- package/payload/server/public/assets/public-Cizdj15i.js +5 -0
- package/payload/server/public/assets/useVoiceRecorder-DIV9KAk_.css +1 -0
- package/payload/server/public/assets/{useVoiceRecorder-CiYPZu3g.js → useVoiceRecorder-tbj4tUsl.js} +1 -1
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +232 -96
- package/payload/server/public/assets/admin-BxVuKRJZ.js +0 -352
- package/payload/server/public/assets/public-Bgm9WQFZ.js +0 -5
- package/payload/server/public/assets/useVoiceRecorder-BORuG_su.css +0 -1
package/package.json
CHANGED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Shared stream-log helpers sourced by setup-tunnel.sh and reset-tunnel.sh.
|
|
3
|
+
#
|
|
4
|
+
# The platform exposes STREAM_LOG_PATH in the `claude` spawn env (Task 556);
|
|
5
|
+
# the Bash-tool subprocess inherits it, and opt-in scripts call these
|
|
6
|
+
# helpers to emit phase lines and tee subprocess output into the same
|
|
7
|
+
# per-conversation file the chat UI's server-side tailer reads.
|
|
8
|
+
#
|
|
9
|
+
# Contract (read by platform/ui/app/api/admin/chat/route.ts tailer and
|
|
10
|
+
# .docs/platform.md):
|
|
11
|
+
# [<ISO-ts>] [<scope>] <kv …>
|
|
12
|
+
# [<ISO-ts>] [<scope>:<subprocess-tag>] <raw line>
|
|
13
|
+
# where <scope> ∈ {setup-tunnel, reset-tunnel}. The tailer regex is
|
|
14
|
+
# ^\[[^]]+\] \[(setup-tunnel|reset-tunnel)(:[^]]+)?\]
|
|
15
|
+
# so any prefix change must be made on both sides atomically.
|
|
16
|
+
|
|
17
|
+
# Exit 1 loudly with the variable name and the invoking scope so direct-SSH
|
|
18
|
+
# invocations fail fast and the operator reads exactly what to set. No
|
|
19
|
+
# silent fallback — criterion 4 of Task 556.
|
|
20
|
+
require_stream_log_path() {
|
|
21
|
+
local scope="$1"
|
|
22
|
+
if [ -z "${STREAM_LOG_PATH:-}" ]; then
|
|
23
|
+
echo "ERROR [${scope}]: STREAM_LOG_PATH environment variable is unset." >&2
|
|
24
|
+
echo " This script tees subprocess output into a per-conversation" >&2
|
|
25
|
+
echo " stream log; it is meaningless without a target path." >&2
|
|
26
|
+
echo " The platform sets STREAM_LOG_PATH automatically for every" >&2
|
|
27
|
+
echo " \`claude\` spawn (Task 556). If you are invoking this" >&2
|
|
28
|
+
echo " script by hand, export it yourself, for example:" >&2
|
|
29
|
+
echo " export STREAM_LOG_PATH=\"\${HOME}/.maxy/logs/manual-invocation.log\"" >&2
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
mkdir -p "$(dirname "${STREAM_LOG_PATH}")"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# ISO-8601 UTC timestamp with millisecond precision.
|
|
36
|
+
stream_log_ts() {
|
|
37
|
+
date -u +"%Y-%m-%dT%H:%M:%S.%3NZ"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Append one phase line to STREAM_LOG_PATH AND echo to stderr so the Bash
|
|
41
|
+
# tool's stderr capture carries it. Both paths matter: the stream log is
|
|
42
|
+
# the live tailer surface; stderr is the exit-time Bash-tool surface.
|
|
43
|
+
#
|
|
44
|
+
# Usage: phase_line <scope> <key=value …>
|
|
45
|
+
phase_line() {
|
|
46
|
+
local scope="$1"; shift
|
|
47
|
+
local ts
|
|
48
|
+
ts="$(stream_log_ts)"
|
|
49
|
+
printf '[%s] [%s] %s\n' "${ts}" "${scope}" "$*" >> "${STREAM_LOG_PATH}"
|
|
50
|
+
printf '[%s] %s\n' "${scope}" "$*" >&2
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Tee a subprocess's combined stdout+stderr into STREAM_LOG_PATH line-by-line
|
|
54
|
+
# with the given tag, mirroring each line to stderr for the Bash tool.
|
|
55
|
+
# Returns the subprocess's exit code.
|
|
56
|
+
#
|
|
57
|
+
# Use for fire-and-forget subprocesses whose stdout does NOT need to be
|
|
58
|
+
# captured by the caller (the caller only cares about the exit code).
|
|
59
|
+
# For subprocesses whose stdout the caller must parse (e.g. JSON output
|
|
60
|
+
# feeding `jq`), use `tee_subprocess_capture` instead.
|
|
61
|
+
#
|
|
62
|
+
# Usage: tee_subprocess <tag> -- <cmd> <args …>
|
|
63
|
+
# Example: tee_subprocess reset-tunnel:cloudflared -- cloudflared --origincert … tunnel delete my-tunnel
|
|
64
|
+
#
|
|
65
|
+
# stdbuf forces line buffering; without it cloudflared holds its stdout in
|
|
66
|
+
# libc's 8 KB buffer until exit and the ≤ 1 s latency criterion fails.
|
|
67
|
+
tee_subprocess() {
|
|
68
|
+
local tag="$1"; shift
|
|
69
|
+
if [ "${1:-}" != "--" ]; then
|
|
70
|
+
echo "tee_subprocess: expected -- before command" >&2
|
|
71
|
+
return 2
|
|
72
|
+
fi
|
|
73
|
+
shift
|
|
74
|
+
# Prefer `stdbuf -oL -eL` to force line buffering on the subprocess; fall
|
|
75
|
+
# back to the bare command when stdbuf is unavailable (macOS dev). On the
|
|
76
|
+
# Pi (Linux), stdbuf is present via coreutils and cloudflared's output
|
|
77
|
+
# reaches the tee as soon as each line is written.
|
|
78
|
+
local -a buf_prefix=()
|
|
79
|
+
if command -v stdbuf >/dev/null 2>&1; then
|
|
80
|
+
buf_prefix=(stdbuf -oL -eL)
|
|
81
|
+
fi
|
|
82
|
+
"${buf_prefix[@]}" "$@" 2>&1 | while IFS= read -r line; do
|
|
83
|
+
local ts
|
|
84
|
+
ts="$(stream_log_ts)"
|
|
85
|
+
printf '[%s] [%s] %s\n' "${ts}" "${tag}" "${line}" >> "${STREAM_LOG_PATH}"
|
|
86
|
+
printf '%s\n' "${line}" >&2
|
|
87
|
+
done
|
|
88
|
+
return "${PIPESTATUS[0]}"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Tee a subprocess's combined stdout+stderr into STREAM_LOG_PATH line-by-line
|
|
92
|
+
# with the given tag, passing each line through on stdout so the caller can
|
|
93
|
+
# capture it with `> file` or `$( … )`. Stderr mirroring is dropped so the
|
|
94
|
+
# caller's stdout is exactly the subprocess's output — `jq` and friends
|
|
95
|
+
# remain parseable.
|
|
96
|
+
#
|
|
97
|
+
# Usage: tee_subprocess_capture <tag> -- <cmd> <args …> > captured.file
|
|
98
|
+
# Example:
|
|
99
|
+
# tee_subprocess_capture reset-tunnel:cloudflared -- \
|
|
100
|
+
# cloudflared --origincert "${CERT}" tunnel list --output json \
|
|
101
|
+
# > "${NAMES_TMP}"
|
|
102
|
+
tee_subprocess_capture() {
|
|
103
|
+
local tag="$1"; shift
|
|
104
|
+
if [ "${1:-}" != "--" ]; then
|
|
105
|
+
echo "tee_subprocess_capture: expected -- before command" >&2
|
|
106
|
+
return 2
|
|
107
|
+
fi
|
|
108
|
+
shift
|
|
109
|
+
# Prefer `stdbuf -oL -eL` to force line buffering on the subprocess; fall
|
|
110
|
+
# back to the bare command when stdbuf is unavailable (macOS dev). On the
|
|
111
|
+
# Pi (Linux), stdbuf is present via coreutils and cloudflared's output
|
|
112
|
+
# reaches the tee as soon as each line is written.
|
|
113
|
+
local -a buf_prefix=()
|
|
114
|
+
if command -v stdbuf >/dev/null 2>&1; then
|
|
115
|
+
buf_prefix=(stdbuf -oL -eL)
|
|
116
|
+
fi
|
|
117
|
+
"${buf_prefix[@]}" "$@" 2>&1 | while IFS= read -r line; do
|
|
118
|
+
local ts
|
|
119
|
+
ts="$(stream_log_ts)"
|
|
120
|
+
printf '[%s] [%s] %s\n' "${ts}" "${tag}" "${line}" >> "${STREAM_LOG_PATH}"
|
|
121
|
+
printf '%s\n' "${line}"
|
|
122
|
+
done
|
|
123
|
+
return "${PIPESTATUS[0]}"
|
|
124
|
+
}
|
|
@@ -19,6 +19,20 @@
|
|
|
19
19
|
|
|
20
20
|
set -euo pipefail
|
|
21
21
|
|
|
22
|
+
# --------------------------------------------------------------------------
|
|
23
|
+
# Shared stream-log helpers (require STREAM_LOG_PATH, phase_line, tee_subprocess).
|
|
24
|
+
# Task 556: any cloudflared subprocess this script spawns is teed line-by-line
|
|
25
|
+
# into the per-conversation stream log so the chat UI tailer renders live
|
|
26
|
+
# progress — same contract as setup-tunnel.sh.
|
|
27
|
+
# --------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
# shellcheck source=_stream-log.sh
|
|
30
|
+
# Resolve symlinks before dirname — ~/reset-tunnel.sh is installed as a symlink
|
|
31
|
+
# (see packages/create-maxy/src/index.ts:installTunnelScripts), so the raw
|
|
32
|
+
# BASH_SOURCE[0] points at $HOME, not the scripts directory where _stream-log.sh lives.
|
|
33
|
+
source "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/_stream-log.sh"
|
|
34
|
+
require_stream_log_path reset-tunnel
|
|
35
|
+
|
|
22
36
|
if [ "$#" -lt 1 ]; then
|
|
23
37
|
echo "Usage: $0 <brand>" >&2
|
|
24
38
|
exit 2
|
|
@@ -28,28 +42,54 @@ BRAND="$1"
|
|
|
28
42
|
CFG_DIR="${HOME}/.${BRAND}/cloudflared"
|
|
29
43
|
CERT="${CFG_DIR}/cert.pem"
|
|
30
44
|
|
|
45
|
+
phase_line reset-tunnel step=start brand="${BRAND}" cfg_dir="${CFG_DIR}"
|
|
46
|
+
|
|
31
47
|
if [ ! -f "${CERT}" ]; then
|
|
48
|
+
phase_line reset-tunnel step=no-cert cfg_dir="${CFG_DIR}"
|
|
32
49
|
echo "No cert.pem at ${CERT} — nothing to delete via CLI."
|
|
33
50
|
echo "Removing ${CFG_DIR} if present."
|
|
34
51
|
rm -rf "${CFG_DIR}"
|
|
52
|
+
phase_line reset-tunnel step=done result=wiped-no-cert
|
|
35
53
|
exit 0
|
|
36
54
|
fi
|
|
37
55
|
|
|
38
56
|
# Delete every tunnel on the account (the cert is per-account for tunnel CRUD).
|
|
39
|
-
|
|
40
|
-
|
|
57
|
+
# cloudflared's list/delete output is teed into STREAM_LOG_PATH; the `list`
|
|
58
|
+
# call uses tee_subprocess_capture so NAMES_TMP receives the JSON for jq to
|
|
59
|
+
# parse. Using the non-capture variant here would strand the JSON in stderr
|
|
60
|
+
# and leave NAMES_TMP empty — every tunnel would silently survive reset.
|
|
61
|
+
phase_line reset-tunnel step=list-tunnels
|
|
62
|
+
NAMES_TMP="$(mktemp -t maxy-reset-tunnel-list.XXXXXX)"
|
|
63
|
+
# shellcheck disable=SC2064
|
|
64
|
+
trap "rm -f '${NAMES_TMP}'" EXIT
|
|
65
|
+
if ! tee_subprocess_capture reset-tunnel:cloudflared -- \
|
|
66
|
+
cloudflared --origincert "${CERT}" tunnel list --output json \
|
|
67
|
+
> "${NAMES_TMP}"; then
|
|
68
|
+
phase_line reset-tunnel step=list-tunnels result=error reason=cloudflared-list-failed
|
|
69
|
+
echo "ERROR: cloudflared tunnel list failed. See stream log for detail." >&2
|
|
70
|
+
exit 1
|
|
71
|
+
fi
|
|
72
|
+
NAMES="$(jq -r '.[]?.name' "${NAMES_TMP}" 2>/dev/null | sort -u || true)"
|
|
41
73
|
if [ -z "${NAMES}" ]; then
|
|
74
|
+
phase_line reset-tunnel step=list-tunnels result=empty
|
|
42
75
|
echo "No tunnels to delete on this brand's account."
|
|
43
76
|
else
|
|
44
77
|
while IFS= read -r NAME; do
|
|
45
78
|
[ -z "${NAME}" ] && continue
|
|
79
|
+
phase_line reset-tunnel step=delete-tunnel name="${NAME}"
|
|
46
80
|
echo "Deleting tunnel: ${NAME}"
|
|
47
|
-
|
|
81
|
+
if ! tee_subprocess reset-tunnel:cloudflared -- \
|
|
82
|
+
cloudflared --origincert "${CERT}" tunnel delete "${NAME}"; then
|
|
83
|
+
phase_line reset-tunnel step=delete-tunnel result=error name="${NAME}"
|
|
84
|
+
echo "ERROR: cloudflared tunnel delete failed for ${NAME}. See stream log." >&2
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
48
87
|
done <<< "${NAMES}"
|
|
49
88
|
fi
|
|
50
89
|
|
|
51
90
|
# Wipe the brand-scoped cloudflared state directory.
|
|
52
91
|
rm -rf "${CFG_DIR}"
|
|
92
|
+
phase_line reset-tunnel step=wipe-cfg-dir path="${CFG_DIR}"
|
|
53
93
|
echo "Removed ${CFG_DIR}"
|
|
54
94
|
|
|
55
95
|
echo ""
|
|
@@ -63,3 +103,5 @@ echo "The platform service (${BRAND}.service) was not touched. To release any"
|
|
|
63
103
|
echo "running cloudflared connector started by ${BRAND}.service's ExecStartPre,"
|
|
64
104
|
echo "either restart the service (which will no-op on resume since tunnel.state"
|
|
65
105
|
echo "is gone) or leave it running until you re-run setup-tunnel.sh."
|
|
106
|
+
|
|
107
|
+
phase_line reset-tunnel step=done result=ok brand="${BRAND}"
|
|
@@ -13,9 +13,28 @@
|
|
|
13
13
|
# via `cloudflared tunnel route dns` — see manual-setup.md §Step 4 Apex
|
|
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
|
+
#
|
|
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:9222 — the 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.
|
|
16
24
|
|
|
17
25
|
set -euo pipefail
|
|
18
26
|
|
|
27
|
+
# --------------------------------------------------------------------------
|
|
28
|
+
# Shared stream-log helpers (require STREAM_LOG_PATH, phase_line, …).
|
|
29
|
+
# --------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
# shellcheck source=_stream-log.sh
|
|
32
|
+
# Resolve symlinks before dirname — ~/setup-tunnel.sh is installed as a symlink
|
|
33
|
+
# (see packages/create-maxy/src/index.ts:installTunnelScripts), so the raw
|
|
34
|
+
# BASH_SOURCE[0] points at $HOME, not the scripts directory where _stream-log.sh lives.
|
|
35
|
+
source "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")/_stream-log.sh"
|
|
36
|
+
require_stream_log_path setup-tunnel
|
|
37
|
+
|
|
19
38
|
# --------------------------------------------------------------------------
|
|
20
39
|
# Args
|
|
21
40
|
# --------------------------------------------------------------------------
|
|
@@ -31,6 +50,8 @@ PORT="$2"
|
|
|
31
50
|
shift 2
|
|
32
51
|
HOSTNAMES=("$@")
|
|
33
52
|
|
|
53
|
+
phase_line setup-tunnel step=start brand="${BRAND}" port="${PORT}" hostnames="${HOSTNAMES[*]}"
|
|
54
|
+
|
|
34
55
|
# --------------------------------------------------------------------------
|
|
35
56
|
# Step 0: Set brand context (paths + dirs). Corresponds to runbook Step 0.
|
|
36
57
|
# --------------------------------------------------------------------------
|
|
@@ -40,24 +61,144 @@ mkdir -p "${CFG_DIR}"
|
|
|
40
61
|
|
|
41
62
|
# --------------------------------------------------------------------------
|
|
42
63
|
# Step 1: OAuth login (only if cert.pem missing). Corresponds to runbook Step 1.
|
|
43
|
-
#
|
|
44
|
-
#
|
|
64
|
+
#
|
|
65
|
+
# Control flow, rewritten per Task 556:
|
|
66
|
+
# 1. CDP precheck on 127.0.0.1:9222 — loud failure if Chromium isn't up.
|
|
67
|
+
# 2. Spawn cloudflared with stdout+stderr teed line-by-line to
|
|
68
|
+
# $STREAM_LOG_PATH with prefix [setup-tunnel:cloudflared].
|
|
69
|
+
# 3. Extract the authorize URL with a tolerant regex as it streams.
|
|
70
|
+
# 4. Drive the VNC Chromium to that URL via CDP `PUT /json/new?<url>`.
|
|
71
|
+
# 5. Wait for ~/.cloudflared/cert.pem to land with bounded timeout.
|
|
72
|
+
# 6. Move cert.pem into the brand-scoped path.
|
|
73
|
+
#
|
|
74
|
+
# Every branch exits 1 loudly naming the failure — no silent retries,
|
|
75
|
+
# no xdg-open race, no cloudflared-internal browser-spawn path.
|
|
45
76
|
# --------------------------------------------------------------------------
|
|
46
77
|
|
|
47
78
|
if [ ! -f "${CFG_DIR}/cert.pem" ]; then
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
79
|
+
phase_line setup-tunnel step=oauth-login cert_path="${CFG_DIR}/cert.pem" display="${DISPLAY:-:99}"
|
|
80
|
+
|
|
81
|
+
# CDP precheck — fail loudly if Chromium DevTools is not answering.
|
|
82
|
+
if ! curl -sf --max-time 2 "http://127.0.0.1:9222/json/version" > /dev/null 2>&1; then
|
|
83
|
+
phase_line setup-tunnel step=oauth-login result=error reason=cdp-unreachable \
|
|
84
|
+
endpoint=http://127.0.0.1:9222 hint="run ~/vnc.sh restart"
|
|
85
|
+
echo "ERROR: Chromium CDP on 127.0.0.1:9222 is not reachable." >&2
|
|
86
|
+
echo " The script needs CDP to drive the authorize URL on the VNC browser." >&2
|
|
87
|
+
echo " Fix: run 'vnc.sh restart' to bring Chromium up on :99." >&2
|
|
88
|
+
exit 1
|
|
89
|
+
fi
|
|
90
|
+
phase_line setup-tunnel step=oauth-login cdp=ok
|
|
91
|
+
|
|
92
|
+
URL_FILE="$(mktemp -t maxy-setup-tunnel-url.XXXXXX)"
|
|
93
|
+
LAST_LINE_FILE="$(mktemp -t maxy-setup-tunnel-last.XXXXXX)"
|
|
94
|
+
: > "${URL_FILE}"
|
|
95
|
+
: > "${LAST_LINE_FILE}"
|
|
96
|
+
# Track the cloudflared pipeline PID so the EXIT trap can kill it on any
|
|
97
|
+
# failure path — including ones that `exit 1` without an explicit kill.
|
|
98
|
+
# Missing this trap leaks a cloudflared subshell waiting for the OAuth
|
|
99
|
+
# callback forever; subsequent setup-tunnel runs see a stale cert.pem
|
|
100
|
+
# landing asynchronously and race against the new URL-extraction pass.
|
|
101
|
+
CF_PIPELINE_PID=""
|
|
102
|
+
cleanup_oauth() {
|
|
103
|
+
[ -n "${CF_PIPELINE_PID}" ] && kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
104
|
+
rm -f "${URL_FILE}" "${LAST_LINE_FILE}"
|
|
105
|
+
}
|
|
106
|
+
trap cleanup_oauth EXIT
|
|
107
|
+
|
|
108
|
+
# cloudflared is line-buffered (stdbuf), teed to the stream log, URL
|
|
109
|
+
# extracted as it streams. The subshell holds the whole pipeline so
|
|
110
|
+
# PIPESTATUS[0] (cloudflared's exit code) is reachable later.
|
|
111
|
+
(
|
|
112
|
+
DISPLAY="${DISPLAY:-:99}" stdbuf -oL -eL cloudflared \
|
|
113
|
+
--origincert "${CFG_DIR}/cert.pem" tunnel login 2>&1 |
|
|
114
|
+
while IFS= read -r line; do
|
|
115
|
+
ts="$(stream_log_ts)"
|
|
116
|
+
printf '[%s] [setup-tunnel:cloudflared] %s\n' "${ts}" "${line}" >> "${STREAM_LOG_PATH}"
|
|
117
|
+
printf '%s\n' "${line}" >&2
|
|
118
|
+
printf '%s\n' "${line}" > "${LAST_LINE_FILE}"
|
|
119
|
+
if [ ! -s "${URL_FILE}" ]; then
|
|
120
|
+
url="$(printf '%s' "${line}" | grep -oE 'https://dash\.cloudflare\.com/argotunnel\?[^ ]+' | head -1 || true)"
|
|
121
|
+
if [ -n "${url}" ]; then
|
|
122
|
+
printf '%s' "${url}" > "${URL_FILE}"
|
|
123
|
+
fi
|
|
124
|
+
fi
|
|
125
|
+
done
|
|
126
|
+
) &
|
|
127
|
+
CF_PIPELINE_PID=$!
|
|
128
|
+
|
|
129
|
+
# Wait up to ~15s for the URL to surface in cloudflared's output.
|
|
130
|
+
URL_WAIT=0
|
|
131
|
+
while [ ! -s "${URL_FILE}" ] && [ "${URL_WAIT}" -lt 30 ]; do
|
|
132
|
+
if ! kill -0 "${CF_PIPELINE_PID}" 2>/dev/null; then
|
|
133
|
+
phase_line setup-tunnel step=oauth-login result=error \
|
|
134
|
+
reason=cloudflared-exited-before-url \
|
|
135
|
+
last_line="$(cat "${LAST_LINE_FILE}" 2>/dev/null || echo none)"
|
|
136
|
+
echo "ERROR: cloudflared exited before printing the authorize URL." >&2
|
|
137
|
+
exit 1
|
|
138
|
+
fi
|
|
139
|
+
sleep 0.5
|
|
140
|
+
URL_WAIT=$((URL_WAIT + 1))
|
|
141
|
+
done
|
|
142
|
+
|
|
143
|
+
if [ ! -s "${URL_FILE}" ]; then
|
|
144
|
+
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
145
|
+
phase_line setup-tunnel step=oauth-login result=error \
|
|
146
|
+
reason=url-not-extracted waited=15s \
|
|
147
|
+
last_line="$(cat "${LAST_LINE_FILE}" 2>/dev/null || echo none)"
|
|
148
|
+
echo "ERROR: cloudflared ran for ~15s without emitting a dash.cloudflare.com/argotunnel URL." >&2
|
|
149
|
+
echo " cloudflared output-format may have changed. Check the stream log tail for the raw lines." >&2
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
AUTH_URL="$(cat "${URL_FILE}")"
|
|
154
|
+
phase_line setup-tunnel step=browser-drive url_extracted=1
|
|
155
|
+
|
|
156
|
+
# Drive CDP. Same PUT /json/new?<url> contract as
|
|
157
|
+
# platform/ui/app/lib/cdp-client.ts (which uses encodeURIComponent on
|
|
158
|
+
# the URL). Without percent-encoding, CDP's URL parser splits on the
|
|
159
|
+
# inner `?` and `&` in the argotunnel URL and drops the callback/token
|
|
160
|
+
# query params — Chromium lands on a bare argotunnel page and the
|
|
161
|
+
# consent screen is never reached. `jq -sRr @uri` percent-encodes a raw
|
|
162
|
+
# string, matching the TS client exactly.
|
|
163
|
+
AUTH_URL_ENC="$(printf '%s' "${AUTH_URL}" | jq -sRr @uri)"
|
|
164
|
+
CDP_RESP="$(curl -sf --max-time 5 -X PUT "http://127.0.0.1:9222/json/new?${AUTH_URL_ENC}" 2>&1 || true)"
|
|
165
|
+
if [ -z "${CDP_RESP}" ]; then
|
|
166
|
+
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
167
|
+
phase_line setup-tunnel step=browser-drive result=error reason=cdp-put-failed
|
|
168
|
+
echo "ERROR: CDP PUT /json/new?<url> returned empty — Chromium rejected the navigate." >&2
|
|
169
|
+
exit 1
|
|
170
|
+
fi
|
|
171
|
+
phase_line setup-tunnel step=browser-drive result=accepted
|
|
172
|
+
|
|
173
|
+
# Wait for cert.pem to land — cloudflared writes to ~/.cloudflared/cert.pem
|
|
174
|
+
# regardless of --origincert, so watch the canonical location.
|
|
175
|
+
LOGIN_TIMEOUT="${SETUP_TUNNEL_LOGIN_TIMEOUT:-180}"
|
|
176
|
+
LOGIN_WAIT=0
|
|
52
177
|
while [ ! -f "${HOME}/.cloudflared/cert.pem" ]; do
|
|
53
|
-
if ! kill -0 "${
|
|
54
|
-
|
|
178
|
+
if ! kill -0 "${CF_PIPELINE_PID}" 2>/dev/null; then
|
|
179
|
+
# Pipeline exited — one more cert.pem probe in case it landed right before exit.
|
|
180
|
+
if [ -f "${HOME}/.cloudflared/cert.pem" ]; then break; fi
|
|
181
|
+
phase_line setup-tunnel step=oauth-login result=error \
|
|
182
|
+
reason=cloudflared-exited-no-cert \
|
|
183
|
+
last_line="$(cat "${LAST_LINE_FILE}" 2>/dev/null || echo none)"
|
|
184
|
+
echo "ERROR: cloudflared exited before cert.pem landed." >&2
|
|
55
185
|
exit 1
|
|
56
186
|
fi
|
|
57
|
-
|
|
187
|
+
if [ "${LOGIN_WAIT}" -ge "${LOGIN_TIMEOUT}" ]; then
|
|
188
|
+
kill "${CF_PIPELINE_PID}" 2>/dev/null || true
|
|
189
|
+
phase_line setup-tunnel step=oauth-login result=error \
|
|
190
|
+
reason=timeout-waiting-cert waited="${LOGIN_WAIT}s" \
|
|
191
|
+
last_line="$(cat "${LAST_LINE_FILE}" 2>/dev/null || echo none)"
|
|
192
|
+
echo "ERROR: Timed out after ${LOGIN_WAIT}s waiting for cert.pem to land." >&2
|
|
193
|
+
exit 1
|
|
194
|
+
fi
|
|
195
|
+
sleep 1
|
|
196
|
+
LOGIN_WAIT=$((LOGIN_WAIT + 1))
|
|
58
197
|
done
|
|
198
|
+
|
|
59
199
|
mv "${HOME}/.cloudflared/cert.pem" "${CFG_DIR}/cert.pem"
|
|
60
|
-
|
|
200
|
+
phase_line setup-tunnel step=oauth-login result=cert-received \
|
|
201
|
+
path="${CFG_DIR}/cert.pem" waited="${LOGIN_WAIT}s"
|
|
61
202
|
fi
|
|
62
203
|
|
|
63
204
|
# --------------------------------------------------------------------------
|
|
@@ -103,6 +103,8 @@ After this, every `console.error("[your-tool] ...")` from any tool in the plugin
|
|
|
103
103
|
|
|
104
104
|
**How the tee decides which file to write to (Task 532):** the platform sets `STREAM_LOG_PATH` as an environment variable on every MCP server spawn, pointing to the conversation-scoped stream log. The MCP server does not know about conversations — it just trusts `STREAM_LOG_PATH`. Multiple concurrent conversations produce multiple concurrent MCP server processes, each teeing to its own file; no cross-conversation leakage.
|
|
105
105
|
|
|
106
|
+
**`STREAM_LOG_PATH` reaches every Claude Code child (Task 556).** The platform now sets `STREAM_LOG_PATH` on the parent `claude` spawn env itself (not only on MCP server envs), so the bundled Bun runtime inherits it and every Bash-tool subprocess the CLI spawns sees it too. Opt-in shell scripts — currently `setup-tunnel.sh` and `reset-tunnel.sh` under `platform/plugins/cloudflare/scripts/` — read the variable, guard against a missing value with a loud exit, and tee subprocess output line-by-line into the same per-conversation file. Each spawn writes one `[spawn-env] STREAM_LOG_PATH=set pid=… conversationId=… site=…` line so the env-propagation is auditable per session. The chat UI tails the same file for lines matching `^\[[^]]+\] \[(setup-tunnel|reset-tunnel)(:[^]]+)?\] ` and emits them as `script_stream` SSE events; see `.docs/web-chat.md` for the contract.
|
|
107
|
+
|
|
106
108
|
**Retrieve MCP diagnostic lines for a conversation:**
|
|
107
109
|
|
|
108
110
|
- All servers: `logs-read { type: "system", conversationId: "..." }` → grep `[mcp:<name>]` on the returned stream log.
|