@rubytech/create-realagent 1.0.632 → 1.0.634
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/dist/index.js +4 -1
- package/package.json +1 -1
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +7 -1
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +130 -28
- package/payload/platform/plugins/docs/references/cloudflare.md +3 -2
- package/payload/server/public/assets/{admin-BntwbBs-.js → admin-Cacbe9nP.js} +2 -2
- package/payload/server/public/assets/{public-Cizdj15i.js → public-CBVU1ErT.js} +1 -1
- package/payload/server/public/assets/useVoiceRecorder-baV6L6a0.css +1 -0
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +22 -8
- package/payload/server/public/assets/useVoiceRecorder-DIV9KAk_.css +0 -1
- /package/payload/server/public/assets/{useVoiceRecorder-tbj4tUsl.js → useVoiceRecorder-xC0cS-GO.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -840,7 +840,10 @@ function installUv() {
|
|
|
840
840
|
}
|
|
841
841
|
console.log(" Installing uv (Python tool runner — required by Neo4j MCP server)...");
|
|
842
842
|
logFile(" uv: installing via astral.sh installer");
|
|
843
|
-
|
|
843
|
+
// astral.sh installer auto-confirms when stdin is not a TTY (our case under
|
|
844
|
+
// systemd-run). Historically we passed `-y`, which the script rejects with
|
|
845
|
+
// "unknown option -y" and causes uv to never install on upgrade.
|
|
846
|
+
const result = spawnSync("bash", ["-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"], { stdio: "inherit" });
|
|
844
847
|
if (result.status !== 0) {
|
|
845
848
|
console.error(` WARNING: uv install exited ${result.status} — graph MCP server will fail at session start until this is retried`);
|
|
846
849
|
logFile(` WARNING: uv install failed with status ${result.status}`);
|
package/package.json
CHANGED
|
@@ -197,7 +197,13 @@ cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns
|
|
|
197
197
|
|
|
198
198
|
**Why:** Creates a CNAME in Cloudflare DNS pointing each hostname at `<UUID>.cfargotunnel.com`. `--overwrite-dns` makes it idempotent.
|
|
199
199
|
|
|
200
|
-
**Success:**
|
|
200
|
+
**Success — any of these output shapes:** cloudflared emits one of three distinct messages on exit 0, all of which mean the hostname is routed correctly. Do not parse stdout to distinguish them — exit 0 is the signal.
|
|
201
|
+
|
|
202
|
+
1. New record: `Added CNAME <hostname> which will route to this tunnel`
|
|
203
|
+
2. Overwritten: `Added CNAME <hostname> which will route to this tunnel` (same shape as new; `--overwrite-dns` makes overwrite transparent)
|
|
204
|
+
3. Idempotent no-op: `<timestamp> INF <hostname> is already configured to route to your tunnel tunnelID=<UUID>`
|
|
205
|
+
|
|
206
|
+
Shape 3 is what a second clean run of setup-tunnel.sh against an already-configured hostname emits. Historically the shell script's stdout parser rejected this shape and exited 1 on the idempotent case (session `25674fe3`, Task 559 fix); the script now relies on cloudflared's exit code exclusively.
|
|
201
207
|
|
|
202
208
|
**If it fails with `zone not found`:** the hostname's parent domain isn't on this brand's Cloudflare account. Either add it in the dashboard (Websites → Add a site) and re-run, or sign into the account that already owns the domain.
|
|
203
209
|
|
|
@@ -203,27 +203,58 @@ fi
|
|
|
203
203
|
|
|
204
204
|
# --------------------------------------------------------------------------
|
|
205
205
|
# Step 2+3: Create tunnel if absent; otherwise reuse. Capture UUID.
|
|
206
|
+
# Emit phase_line step=tunnel-resolve with action=reused|created so the
|
|
207
|
+
# stream log tailer shows which tunnel identity Steps 4+5 are writing
|
|
208
|
+
# against (Task 559 — Bug B: previously a bare `echo` that only surfaced
|
|
209
|
+
# in the Bash tool_result after subprocess exit).
|
|
206
210
|
# --------------------------------------------------------------------------
|
|
207
211
|
|
|
208
212
|
TUNNEL_NAME="${BRAND}-$(hostname -s)"
|
|
209
213
|
TUNNEL_ID="$(cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel list --output json 2>/dev/null \
|
|
210
214
|
| jq -r --arg N "${TUNNEL_NAME}" '.[]? | select(.name == $N) | .id' | head -1)"
|
|
215
|
+
TUNNEL_ACTION="reused"
|
|
211
216
|
if [ -z "${TUNNEL_ID}" ] || [ "${TUNNEL_ID}" = "null" ]; then
|
|
212
217
|
cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel create "${TUNNEL_NAME}"
|
|
213
218
|
TUNNEL_ID="$(cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel list --output json \
|
|
214
219
|
| jq -r --arg N "${TUNNEL_NAME}" '.[]? | select(.name == $N) | .id' | head -1)"
|
|
220
|
+
TUNNEL_ACTION="created"
|
|
215
221
|
fi
|
|
216
222
|
if [ -z "${TUNNEL_ID}" ] || [ "${TUNNEL_ID}" = "null" ]; then
|
|
223
|
+
phase_line setup-tunnel step=tunnel-resolve result=error \
|
|
224
|
+
reason=uuid-missing tunnel_name="${TUNNEL_NAME}"
|
|
217
225
|
echo "ERROR: failed to create or find tunnel ${TUNNEL_NAME}" >&2
|
|
218
226
|
exit 1
|
|
219
227
|
fi
|
|
228
|
+
phase_line setup-tunnel step=tunnel-resolve tunnel_name="${TUNNEL_NAME}" \
|
|
229
|
+
tunnel_id="${TUNNEL_ID}" action="${TUNNEL_ACTION}"
|
|
220
230
|
echo "tunnel: ${TUNNEL_NAME} (${TUNNEL_ID})"
|
|
221
231
|
|
|
222
232
|
# --------------------------------------------------------------------------
|
|
223
|
-
# Step
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
#
|
|
233
|
+
# Step 3b: Zone pre-flight. Before routing DNS, verify every non-apex
|
|
234
|
+
# hostname's registrable parent (last two labels, e.g. rogerblack.maxy.bot
|
|
235
|
+
# → maxy.bot) has NS records pointing at Cloudflare. If any hostname's
|
|
236
|
+
# parent zone is not on Cloudflare, refuse the whole run before calling
|
|
237
|
+
# `cloudflared tunnel route dns`.
|
|
238
|
+
#
|
|
239
|
+
# DESIGN NOTE — what this catches and what it does NOT catch (Task 559):
|
|
240
|
+
# CATCHES: parent zone does not exist, or its NS records do not point
|
|
241
|
+
# at Cloudflare's nameservers. Pre-529 the shell relied on a post-
|
|
242
|
+
# flight sed of cloudflared's stdout for this defence; Task 559
|
|
243
|
+
# deletes that parser because it rejects the idempotent no-op output
|
|
244
|
+
# shape (session 25674fe3) and replaces it with this inline NS probe.
|
|
245
|
+
# Same primitive the MCP path uses in
|
|
246
|
+
# cloudflared.ts::checkZoneParentOnCloudflare.
|
|
247
|
+
# DOES NOT CATCH: the zone is on Cloudflare but on a DIFFERENT account
|
|
248
|
+
# than the one cert.pem is bound to. A true account-zone-list check
|
|
249
|
+
# requires either a cloudflared CLI zone-list subcommand (does not
|
|
250
|
+
# exist as of 2026-04) or persisting the bound account's zones at
|
|
251
|
+
# tunnel-login time (deferred — separate task). The wrong-account
|
|
252
|
+
# case is detected post-hoc by tunnel-status's hostname probe, not
|
|
253
|
+
# here. This is an explicitly accepted gap per Task 559's scope.
|
|
254
|
+
#
|
|
255
|
+
# Probe uses 1.1.1.1 directly to bypass the device's local resolver
|
|
256
|
+
# (matching Resolver.setServers in the MCP path) — avoids cache /
|
|
257
|
+
# split-horizon issues on the Pi.
|
|
227
258
|
# --------------------------------------------------------------------------
|
|
228
259
|
|
|
229
260
|
is_apex() {
|
|
@@ -233,39 +264,110 @@ is_apex() {
|
|
|
233
264
|
[ "$(echo -n "$h" | tr -cd '.' | wc -c)" = "1" ]
|
|
234
265
|
}
|
|
235
266
|
|
|
267
|
+
registrable_parent() {
|
|
268
|
+
local h="$1"
|
|
269
|
+
local labels n
|
|
270
|
+
IFS='.' read -ra labels <<< "${h}"
|
|
271
|
+
n=${#labels[@]}
|
|
272
|
+
if [ "${n}" -le 2 ]; then
|
|
273
|
+
printf '%s' "${h}"
|
|
274
|
+
else
|
|
275
|
+
printf '%s.%s' "${labels[$((n-2))]}" "${labels[$((n-1))]}"
|
|
276
|
+
fi
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if ! command -v dig >/dev/null 2>&1; then
|
|
280
|
+
phase_line setup-tunnel step=zone-preflight result=error \
|
|
281
|
+
reason=dig-missing
|
|
282
|
+
echo "ERROR: dig is not in PATH — required for the zone pre-flight check." >&2
|
|
283
|
+
echo " Install DNS tooling: sudo apt-get install -y bind9-dnsutils" >&2
|
|
284
|
+
exit 1
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
ZONES_SEEN=""
|
|
288
|
+
MISSING_PARENT=""
|
|
289
|
+
for H in "${HOSTNAMES[@]}"; do
|
|
290
|
+
if is_apex "$H"; then continue; fi
|
|
291
|
+
ZONE="$(registrable_parent "$H")"
|
|
292
|
+
NS_OUT="$(dig +short +time=3 +tries=1 NS "${ZONE}" @1.1.1.1 2>/dev/null || true)"
|
|
293
|
+
if printf '%s' "${NS_OUT}" | grep -qiE '\.ns\.cloudflare\.com\.?$'; then
|
|
294
|
+
case ",${ZONES_SEEN}," in
|
|
295
|
+
*",${ZONE},"*) ;;
|
|
296
|
+
*) ZONES_SEEN="${ZONES_SEEN:+${ZONES_SEEN},}${ZONE}" ;;
|
|
297
|
+
esac
|
|
298
|
+
else
|
|
299
|
+
MISSING_PARENT="${H}"
|
|
300
|
+
break
|
|
301
|
+
fi
|
|
302
|
+
done
|
|
303
|
+
|
|
304
|
+
if [ -n "${MISSING_PARENT}" ]; then
|
|
305
|
+
MISSING_ZONE="$(registrable_parent "${MISSING_PARENT}")"
|
|
306
|
+
phase_line setup-tunnel step=zone-preflight result=error \
|
|
307
|
+
missing_parent_for="${MISSING_PARENT}" \
|
|
308
|
+
zones_on_account="${ZONES_SEEN}"
|
|
309
|
+
echo "" >&2
|
|
310
|
+
echo "ERROR: cannot route ${MISSING_PARENT} — its parent zone ${MISSING_ZONE}" >&2
|
|
311
|
+
echo " is not on Cloudflare (NS records do not point at *.ns.cloudflare.com)." >&2
|
|
312
|
+
echo " Zones confirmed on Cloudflare so far: ${ZONES_SEEN:-none}" >&2
|
|
313
|
+
echo "" >&2
|
|
314
|
+
echo " Fix: sign into the Cloudflare account that owns ${MISSING_ZONE}" >&2
|
|
315
|
+
echo " 1. ~/reset-tunnel.sh # clear cert.pem and tunnel state" >&2
|
|
316
|
+
echo " 2. ~/setup-tunnel.sh ... # re-run while signed into the correct account" >&2
|
|
317
|
+
exit 1
|
|
318
|
+
fi
|
|
319
|
+
|
|
320
|
+
phase_line setup-tunnel step=zone-preflight result=ok \
|
|
321
|
+
zones_on_account="${ZONES_SEEN}"
|
|
322
|
+
|
|
323
|
+
# --------------------------------------------------------------------------
|
|
324
|
+
# Step 4: Route DNS. Apex hostnames (exactly two DNS labels) cannot be
|
|
325
|
+
# routed via `cloudflared tunnel route dns` — it misroutes them into
|
|
326
|
+
# another zone on the account. Skip CLI routing for apex; collect for the
|
|
327
|
+
# ACTION REQUIRED summary at the end.
|
|
328
|
+
#
|
|
329
|
+
# Control flow (Task 559): cloudflared's exit code is the sole decision
|
|
330
|
+
# signal. No stdout parsing. `cloudflared tunnel route dns --overwrite-dns`
|
|
331
|
+
# exits 0 on every legitimate outcome (create, overwrite, already-correct
|
|
332
|
+
# no-op) and non-zero on every legitimate failure. The pre-flight above
|
|
333
|
+
# already refused if the parent zone is not on Cloudflare; the post-flight
|
|
334
|
+
# parser the shell historically carried (deleted in 559) rejected the
|
|
335
|
+
# idempotent no-op output shape `INF <h> is already configured to
|
|
336
|
+
# route...` and caused session 25674fe3 to die after cloudflared exited 0.
|
|
337
|
+
# --------------------------------------------------------------------------
|
|
338
|
+
|
|
236
339
|
APEX_HOSTNAMES=()
|
|
237
340
|
for H in "${HOSTNAMES[@]}"; do
|
|
238
341
|
if is_apex "$H"; then
|
|
239
342
|
APEX_HOSTNAMES+=("$H")
|
|
343
|
+
phase_line setup-tunnel step=route-dns hostname="${H}" result=apex-skip
|
|
240
344
|
echo "apex ${H} — skipping CLI DNS routing (manual dashboard step required)"
|
|
241
345
|
continue
|
|
242
346
|
fi
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
#
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
|
|
251
|
-
if
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
echo "
|
|
262
|
-
echo "
|
|
263
|
-
|
|
264
|
-
echo " (then pick the correct zone in the dashboard consent screen)" >&2
|
|
265
|
-
echo " mv ~/.cloudflared/cert.pem ${CFG_DIR}/cert.pem" >&2
|
|
266
|
-
echo " 3. Re-run this script." >&2
|
|
347
|
+
|
|
348
|
+
phase_line setup-tunnel step=route-dns hostname="${H}" tunnel_id="${TUNNEL_ID}"
|
|
349
|
+
ROUTE_LOG="$(mktemp -t maxy-route-dns.XXXXXX)"
|
|
350
|
+
# tee_subprocess_capture streams cloudflared's combined stdout+stderr
|
|
351
|
+
# into STREAM_LOG_PATH line-by-line with the [setup-tunnel:cloudflared]
|
|
352
|
+
# tag (live-tailable) AND passes the same output through this shell's
|
|
353
|
+
# stdout so the `> "${ROUTE_LOG}"` redirection can capture it for the
|
|
354
|
+
# failure-path phase_line. Exit code is cloudflared's PIPESTATUS[0].
|
|
355
|
+
if tee_subprocess_capture setup-tunnel:cloudflared -- \
|
|
356
|
+
cloudflared --origincert "${CFG_DIR}/cert.pem" \
|
|
357
|
+
tunnel route dns --overwrite-dns "${TUNNEL_ID}" "${H}" \
|
|
358
|
+
> "${ROUTE_LOG}"; then
|
|
359
|
+
phase_line setup-tunnel step=route-dns hostname="${H}" result=ok
|
|
360
|
+
else
|
|
361
|
+
ROUTE_RC=$?
|
|
362
|
+
STDERR_BOUNDED="$(tr '\n' ' ' < "${ROUTE_LOG}" | head -c 400)"
|
|
363
|
+
phase_line setup-tunnel step=route-dns hostname="${H}" result=error \
|
|
364
|
+
exit="${ROUTE_RC}" stderr="${STDERR_BOUNDED}"
|
|
365
|
+
echo "ERROR: cloudflared tunnel route dns failed for ${H} (exit=${ROUTE_RC})" >&2
|
|
366
|
+
echo " stderr: ${STDERR_BOUNDED}" >&2
|
|
367
|
+
rm -f "${ROUTE_LOG}"
|
|
267
368
|
exit 1
|
|
268
369
|
fi
|
|
370
|
+
rm -f "${ROUTE_LOG}"
|
|
269
371
|
done
|
|
270
372
|
|
|
271
373
|
# --------------------------------------------------------------------------
|
|
@@ -24,8 +24,9 @@ Ask the agent to set up Cloudflare. The agent collects four things before acting
|
|
|
24
24
|
The agent then invokes `setup-tunnel.sh` on the device with your inputs. The script runs end-to-end:
|
|
25
25
|
|
|
26
26
|
- `cloudflared tunnel login` — OAuth browser sign-in. The VNC browser opens the Cloudflare authorize page; pick the account that owns your domain, click Authorize. `cert.pem` lands.
|
|
27
|
-
- Tunnel creation under the naming convention `{brand}-{hostname}` (e.g. `maxy-neo`).
|
|
28
|
-
-
|
|
27
|
+
- Tunnel creation under the naming convention `{brand}-{hostname}` (e.g. `maxy-neo`). Stream log emits `step=tunnel-resolve action=reused|created` once the UUID is known so the admin agent can see which tunnel the later steps will write against.
|
|
28
|
+
- **Zone pre-flight** — for every non-apex hostname the script queries `1.1.1.1` for the registrable parent's NS records and refuses the whole run if they don't point at Cloudflare. Stream log: `step=zone-preflight result=ok|error zones_on_account=… missing_parent_for=…`. Catches "domain not on Cloudflare"; does not catch "domain on a different Cloudflare account than `cert.pem` is bound to" — that case surfaces later via `tunnel-status`.
|
|
29
|
+
- `cloudflared tunnel route dns` for each subdomain hostname. Apex hostnames cannot be routed this way — the script prints an **ACTION REQUIRED** block naming the exact dashboard record to add or edit. Stream log emits `step=route-dns hostname=… tunnel_id=…` before the call and `step=route-dns hostname=… result=ok|apex-skip|error` after; on error the bounded cloudflared stderr (≤400 chars) rides in the same phase line. **The script does not parse cloudflared's stdout** — exit code is the sole decision signal, so all three legitimate cloudflared output shapes (new record, overwrite, idempotent "already configured") are treated as success.
|
|
29
30
|
- `config.yml` and `tunnel.state` written under `${CFG_DIR}`.
|
|
30
31
|
- `systemctl --user restart ${BRAND}.service` — restarts the platform service so the new tunnel spawns via the service's `ExecStartPre=resume-tunnel.sh`.
|
|
31
32
|
- Post-restart verification — `ps -ef | grep '[c]loudflared'` confirms the connector is alive, then `curl -I https://<hostname>` against each subdomain (up to 60 s per host) confirms a non-530 response.
|