@rubytech/create-realagent 1.0.615 → 1.0.617

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.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts +23 -13
  3. package/payload/platform/lib/mcp-stderr-tee/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/mcp-stderr-tee/dist/index.js +86 -89
  5. package/payload/platform/lib/mcp-stderr-tee/dist/index.js.map +1 -1
  6. package/payload/platform/lib/mcp-stderr-tee/src/index.ts +86 -101
  7. package/payload/platform/package-lock.json +1547 -1
  8. package/payload/platform/plugins/admin/mcp/dist/index.js +33 -2
  9. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  10. package/payload/platform/plugins/admin/skills/stream-log-review/SKILL.md +22 -8
  11. package/payload/platform/plugins/cloudflare/PLUGIN.md +5 -4
  12. package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-binding.test.ts +195 -0
  13. package/payload/platform/plugins/cloudflare/mcp/dist/index.js +160 -214
  14. package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
  15. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts +203 -42
  16. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.d.ts.map +1 -1
  17. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js +623 -195
  18. package/payload/platform/plugins/cloudflare/mcp/dist/lib/cloudflared.js.map +1 -1
  19. package/payload/platform/plugins/cloudflare/mcp/package.json +5 -2
  20. package/payload/platform/plugins/cloudflare/mcp/vitest.config.ts +10 -0
  21. package/payload/platform/plugins/cloudflare/references/setup-guide.md +26 -30
  22. package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +28 -4
  23. package/payload/platform/plugins/docs/PLUGIN.md +2 -0
  24. package/payload/platform/plugins/docs/references/cloudflare.md +51 -0
  25. package/payload/platform/plugins/docs/references/plugins-guide.md +8 -6
  26. package/payload/platform/scripts/logs-read.sh +114 -54
  27. package/payload/platform/templates/specialists/agents/personal-assistant.md +12 -8
  28. package/payload/server/server.js +387 -70
@@ -1,19 +1,28 @@
1
1
  #!/usr/bin/env bash
2
2
  # logs-read.sh — Shell counterpart to the logs-read MCP tool.
3
3
  #
4
- # Mirrors the MCP tool's exact behaviour so the dev agent can retrieve
5
- # session logs from the Pi over SSH with one call instead of constructing
6
- # ad-hoc grep pipelines.
4
+ # Task 532: stream logs are per-conversation, not per-day. The primary mode
5
+ # now reads a single {prefix}-{conversationId}.log file the reader gets a
6
+ # continuous, conversation-scoped timeline from first [spawn] to final
7
+ # [process-exit] without needing to filter.
7
8
  #
8
- # Usage:
9
- # logs-read.sh <sessionKey> [type] Session-key mode (grep across log files)
10
- # logs-read.sh --tail [type] [lines] Tail most recent file of specified type
9
+ # Modes:
10
+ # logs-read.sh <conversationId> [type] Read {prefix}-{convId}.log
11
+ # logs-read.sh --tail [type] [lines] Tail most recent file of type
12
+ # logs-read.sh --grep <sessionKey> [type] Legacy grep across log files
11
13
  #
12
- # Types: system, session, error, heartbeat, public, server
14
+ # Types: system, session, error, heartbeat, public, server, mcp, vnc, review
15
+ # For per-conversation types (system/session/error/public): conversationId is
16
+ # required in the first mode. For platform-scoped types (server/vnc/review/
17
+ # heartbeat): conversationId is ignored.
18
+ #
19
+ # Every invocation writes a one-line trailer describing files searched,
20
+ # matches returned, and files rejected — empty output never leaves a reader
21
+ # guessing why.
13
22
  #
14
23
  # Exit codes:
15
24
  # 0 Success (output produced)
16
- # 1 No matches found (clean, not an error)
25
+ # 1 No matches / file not found (clean, not an error)
17
26
  # 2 Usage error or missing directory
18
27
  set -euo pipefail
19
28
 
@@ -50,7 +59,6 @@ if [[ ${#ACCOUNT_DIRS[@]} -eq 0 ]]; then
50
59
  fi
51
60
 
52
61
  # Build array of account log directories that actually exist.
53
- # Multi-account devices (e.g. Maxy Pi) may have >1 account dir.
54
62
  ACCOUNT_LOG_DIRS=()
55
63
  for _adir in "${ACCOUNT_DIRS[@]}"; do
56
64
  [[ -d "${_adir}logs" ]] && ACCOUNT_LOG_DIRS+=("${_adir}logs")
@@ -62,20 +70,15 @@ fi
62
70
  MULTI_ACCOUNT=$(( ${#ACCOUNT_LOG_DIRS[@]} > 1 ? 1 : 0 ))
63
71
 
64
72
  # Account ID suffix for headers — only shown when multiple accounts exist.
65
- # Accepts a log directory path (e.g. /path/accounts/<id>/logs) and extracts
66
- # the account ID from the parent directory name.
67
73
  account_suffix() {
68
74
  if [[ $MULTI_ACCOUNT -eq 1 ]]; then
69
75
  local path="$1"
70
- # Normalize: strip trailing slash, ensure we're looking at the logs dir
71
76
  path="${path%/}"
72
- # If path ends with /logs, extract account ID from parent
73
77
  if [[ "$path" == */logs ]]; then
74
78
  local acct_id
75
79
  acct_id=$(basename "$(dirname "$path")")
76
80
  echo " [${acct_id:0:8}]"
77
81
  else
78
- # Path is a file inside logs/ — go up two levels
79
82
  local acct_id
80
83
  acct_id=$(basename "$(dirname "$(dirname "$path")")")
81
84
  echo " [${acct_id:0:8}]"
@@ -84,7 +87,7 @@ account_suffix() {
84
87
  }
85
88
 
86
89
  # --- Prefix map (mirrors MCP tool exactly) ---
87
- # Function instead of associative array for bash 3.x compatibility (macOS testing).
90
+ # Per-conversation types: stream log + its siblings (stderr, sse, public).
88
91
  prefix_for_type() {
89
92
  case "$1" in
90
93
  system) echo "claude-agent-stream-" ;;
@@ -96,24 +99,34 @@ prefix_for_type() {
96
99
  esac
97
100
  }
98
101
 
99
- # All searchable types (excludes heartbeat no session context in heartbeat logs)
102
+ # Which types are per-conversation (the first mode's convId argument applies)
103
+ is_per_conversation_type() {
104
+ case "$1" in
105
+ system|error|session|public) return 0 ;;
106
+ *) return 1 ;;
107
+ esac
108
+ }
109
+
100
110
  SEARCH_TYPES="system error session public server mcp vnc review"
101
111
  VALID_TYPES="system, session, error, heartbeat, public, server, mcp, vnc, review"
102
112
 
103
- # --- Usage ---
104
113
  usage() {
105
114
  cat >&2 <<EOF
106
115
  Usage:
107
- logs-read.sh <sessionKey> [type] Grep logs for session key
108
- logs-read.sh --tail [type] [lines] Tail most recent log file
116
+ logs-read.sh <conversationId> [type] Read {prefix}-{convId}.log
117
+ logs-read.sh --tail [type] [lines] Tail most recent log of type
118
+ logs-read.sh --grep <sessionKey> [type] Legacy grep across log files
109
119
 
110
120
  Types: $VALID_TYPES
111
121
  Default type: system | Default lines: 50
122
+
123
+ Per-conversation types (system, session, error, public) require conversationId
124
+ in the first mode — the log file is named {prefix}-{conversationId}.log.
125
+ Platform-scoped types (server, vnc, review, heartbeat) ignore conversationId.
112
126
  EOF
113
127
  exit 2
114
128
  }
115
129
 
116
- # --- Validate type ---
117
130
  validate_type() {
118
131
  local t="$1"
119
132
  case "$t" in
@@ -122,12 +135,65 @@ validate_type() {
122
135
  esac
123
136
  }
124
137
 
125
- # --- Session-key mode ---
126
- session_key_mode() {
138
+ # --- Per-conversation mode: read {prefix}-{convId}.log ---
139
+ per_conversation_mode() {
140
+ local conv_id="$1"
141
+ local filter_type="${2:-system}"
142
+
143
+ if [[ -z "$conv_id" ]]; then
144
+ echo "Error: conversationId cannot be empty" >&2
145
+ exit 2
146
+ fi
147
+
148
+ validate_type "$filter_type"
149
+
150
+ # Platform-scoped types shortcut to their fixed files regardless of convId.
151
+ case "$filter_type" in
152
+ server|vnc|review|heartbeat|mcp)
153
+ echo "# note: type=$filter_type is platform-scoped; conversationId ignored — showing fixed file" >&2
154
+ tail_mode "$filter_type" "50"
155
+ return
156
+ ;;
157
+ esac
158
+
159
+ local prefix
160
+ prefix=$(prefix_for_type "$filter_type")
161
+ if [[ -z "$prefix" ]]; then
162
+ echo "Error: no prefix mapping for type '$filter_type'" >&2
163
+ exit 2
164
+ fi
165
+
166
+ local found=0
167
+ local searched=0
168
+ local rejected=0
169
+ for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
170
+ local filepath="$log_dir/${prefix}${conv_id}.log"
171
+ searched=$((searched + 1))
172
+ if [[ ! -f "$filepath" ]]; then
173
+ rejected=$((rejected + 1))
174
+ continue
175
+ fi
176
+ [[ $found -gt 0 ]] && echo ""
177
+ echo "## $(basename "$filepath") ($filter_type)$(account_suffix "$log_dir")"
178
+ cat "$filepath"
179
+ found=$((found + 1))
180
+ done
181
+
182
+ # Trailer: empty output never leaves the reader guessing why.
183
+ if [[ $found -eq 0 ]]; then
184
+ echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched found=0 rejected=$rejected reason=file-not-found" >&2
185
+ exit 1
186
+ fi
187
+ echo "" >&2
188
+ echo "-- trailer: conversationId=$conv_id type=$filter_type searched=$searched matched=$found rejected=$rejected" >&2
189
+ exit 0
190
+ }
191
+
192
+ # --- Legacy grep mode (backward compatibility with sessionKey-tagged lines) ---
193
+ grep_mode() {
127
194
  local session_key="$1"
128
195
  local filter_type="${2:-}"
129
196
 
130
- # Guard against empty session key (grep -F "" matches everything)
131
197
  if [[ -z "$session_key" ]]; then
132
198
  echo "Error: session key cannot be empty" >&2
133
199
  exit 2
@@ -137,7 +203,6 @@ session_key_mode() {
137
203
  validate_type "$filter_type"
138
204
  fi
139
205
 
140
- # Determine which types to search
141
206
  local types_to_search
142
207
  if [[ -n "$filter_type" && "$filter_type" != "heartbeat" ]]; then
143
208
  types_to_search="$filter_type"
@@ -146,11 +211,13 @@ session_key_mode() {
146
211
  fi
147
212
 
148
213
  local found=0
214
+ local files_searched=0
215
+ local files_rejected=0
149
216
 
150
217
  for log_type in $types_to_search; do
151
- # server: single file in configDir (platform-scoped, not account-scoped)
152
218
  if [[ "$log_type" == "server" ]]; then
153
219
  if [[ -f "$SERVER_LOG" ]]; then
220
+ files_searched=$((files_searched + 1))
154
221
  local result
155
222
  if result=$(grep -F "$session_key" "$SERVER_LOG" 2>/dev/null); then
156
223
  if [[ -n "$result" ]]; then
@@ -160,14 +227,15 @@ session_key_mode() {
160
227
  found=$((found + 1))
161
228
  fi
162
229
  fi
230
+ else
231
+ files_rejected=$((files_rejected + 1))
163
232
  fi
164
233
  continue
165
234
  fi
166
235
 
167
- # vnc: single platform-scoped file covering the VNC browser viewer
168
- # lifecycle (boot events from vnc.sh + runtime events from vnc-logger.ts)
169
236
  if [[ "$log_type" == "vnc" ]]; then
170
237
  if [[ -f "$VNC_LOG" ]]; then
238
+ files_searched=$((files_searched + 1))
171
239
  local result
172
240
  if result=$(grep -F "$session_key" "$VNC_LOG" 2>/dev/null); then
173
241
  if [[ -n "$result" ]]; then
@@ -177,16 +245,15 @@ session_key_mode() {
177
245
  found=$((found + 1))
178
246
  fi
179
247
  fi
248
+ else
249
+ files_rejected=$((files_rejected + 1))
180
250
  fi
181
251
  continue
182
252
  fi
183
253
 
184
- # review: single platform-scoped file containing every decision made
185
- # by the in-process review detector — scan cycles, matches, suppressions,
186
- # rate-limit decisions, rule mutations, and admin tool audit trail.
187
- # Task 385 — the one log that proves observability-plus-review is working.
188
254
  if [[ "$log_type" == "review" ]]; then
189
255
  if [[ -f "$REVIEW_LOG" ]]; then
256
+ files_searched=$((files_searched + 1))
190
257
  local result
191
258
  if result=$(grep -F "$session_key" "$REVIEW_LOG" 2>/dev/null); then
192
259
  if [[ -n "$result" ]]; then
@@ -196,6 +263,8 @@ session_key_mode() {
196
263
  found=$((found + 1))
197
264
  fi
198
265
  fi
266
+ else
267
+ files_rejected=$((files_rejected + 1))
199
268
  fi
200
269
  continue
201
270
  fi
@@ -204,20 +273,17 @@ session_key_mode() {
204
273
  prefix=$(prefix_for_type "$log_type")
205
274
  [[ -z "$prefix" ]] && continue
206
275
 
207
- # Search all account log directories for matching files
208
276
  for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
209
277
  shopt -s nullglob
210
278
  local files=("$log_dir"/${prefix}*.log)
211
279
  shopt -u nullglob
212
-
213
- # Sort by filename (date suffix ensures chronological order)
214
280
  IFS=$'\n' files=($(printf '%s\n' "${files[@]}" | sort)); unset IFS
215
281
 
216
282
  for filepath in "${files[@]}"; do
283
+ files_searched=$((files_searched + 1))
217
284
  local file
218
285
  file=$(basename "$filepath")
219
286
 
220
- # grep -F: fixed string match. Exit 1 = no match (expected).
221
287
  local result
222
288
  if result=$(grep -F "$session_key" "$filepath" 2>/dev/null); then
223
289
  if [[ -n "$result" ]]; then
@@ -232,27 +298,26 @@ session_key_mode() {
232
298
  done
233
299
 
234
300
  if [[ $found -eq 0 ]]; then
235
- echo "No log lines found for session key \"$session_key\"."
301
+ echo "No log lines found for session key \"$session_key\"." >&2
302
+ echo "-- trailer: sessionKey=$session_key files_searched=$files_searched files_rejected=$files_rejected matches=0" >&2
236
303
  exit 1
237
304
  fi
238
-
305
+ echo "" >&2
306
+ echo "-- trailer: sessionKey=$session_key files_searched=$files_searched files_rejected=$files_rejected matches=$found" >&2
239
307
  exit 0
240
308
  }
241
309
 
242
- # --- Tail mode ---
243
310
  tail_mode() {
244
311
  local log_type="${1:-system}"
245
312
  local lines="${2:-50}"
246
313
 
247
314
  validate_type "$log_type"
248
315
 
249
- # Validate lines is numeric
250
316
  if ! [[ "$lines" =~ ^[0-9]+$ ]]; then
251
317
  echo "Error: lines must be a number, got '$lines'" >&2
252
318
  exit 2
253
319
  fi
254
320
 
255
- # Heartbeat: single fixed file (account-scoped) — search all accounts, use most recent
256
321
  if [[ "$log_type" == "heartbeat" ]]; then
257
322
  local hb_file=""
258
323
  for log_dir in "${ACCOUNT_LOG_DIRS[@]}"; do
@@ -273,7 +338,6 @@ tail_mode() {
273
338
  exit 0
274
339
  fi
275
340
 
276
- # Server: single fixed file (platform-scoped, in configDir)
277
341
  if [[ "$log_type" == "server" ]]; then
278
342
  if [[ ! -f "$SERVER_LOG" ]]; then
279
343
  echo "No server log found at $SERVER_LOG"
@@ -285,11 +349,6 @@ tail_mode() {
285
349
  exit 0
286
350
  fi
287
351
 
288
- # VNC: single platform-scoped file covering the entire VNC browser
289
- # viewer lifecycle (Xtigervnc boot, websockify, Chromium CDP, HTTP
290
- # viewer fetches, WebSocket upgrades, proxy pipe events, MCP browser
291
- # tool calls, client disconnect reasons, ensureVnc/ensureCdp recovery).
292
- # Single retrieval must diagnose any browser-viewer failure mode.
293
352
  if [[ "$log_type" == "vnc" ]]; then
294
353
  if [[ ! -f "$VNC_LOG" ]]; then
295
354
  echo "No vnc-boot log found at $VNC_LOG"
@@ -301,10 +360,6 @@ tail_mode() {
301
360
  exit 0
302
361
  fi
303
362
 
304
- # Review: single platform-scoped file written by the review detector
305
- # (Task 385). Every scan cycle, rule match, suppression decision, rate
306
- # limit hold-back, and admin-tool rule mutation is logged here. This is
307
- # the canonical log to audit "did the detector see this?" questions.
308
363
  if [[ "$log_type" == "review" ]]; then
309
364
  if [[ ! -f "$REVIEW_LOG" ]]; then
310
365
  echo "No review log found at $REVIEW_LOG (detector may not have started yet)"
@@ -316,7 +371,6 @@ tail_mode() {
316
371
  exit 0
317
372
  fi
318
373
 
319
- # Find most recent file by mtime across all account log directories
320
374
  local prefix
321
375
  prefix=$(prefix_for_type "$log_type")
322
376
 
@@ -333,7 +387,9 @@ tail_mode() {
333
387
  exit 1
334
388
  fi
335
389
 
336
- # Sort by mtime descending — most recent first
390
+ # Sort by mtime descending — most recent first. Per-conversation filenames
391
+ # no longer sort chronologically by name (UUID suffix), so mtime is the
392
+ # only reliable "most recently active" ordering.
337
393
  local match
338
394
  match=$(ls -t "${all_files[@]}" | head -1)
339
395
  local match_name
@@ -345,7 +401,6 @@ tail_mode() {
345
401
  exit 0
346
402
  }
347
403
 
348
- # --- Main ---
349
404
  if [[ $# -eq 0 ]]; then
350
405
  usage
351
406
  fi
@@ -355,6 +410,11 @@ case "$1" in
355
410
  shift
356
411
  tail_mode "${1:-system}" "${2:-50}"
357
412
  ;;
413
+ --grep)
414
+ shift
415
+ [[ $# -eq 0 ]] && usage
416
+ grep_mode "$1" "${2:-}"
417
+ ;;
358
418
  --help|-h)
359
419
  usage
360
420
  ;;
@@ -363,6 +423,6 @@ case "$1" in
363
423
  usage
364
424
  ;;
365
425
  *)
366
- session_key_mode "$1" "${2:-}"
426
+ per_conversation_mode "$1" "${2:-}"
367
427
  ;;
368
428
  esac
@@ -3,7 +3,7 @@ name: personal-assistant
3
3
  description: "Your personal assistant — scheduling, platform administration, messaging channels, system health, and browser automation. Delegate when a task involves managing your calendar, configuring the platform, operating messaging channels, or completing interactive browser tasks."
4
4
  summary: "Handles the operational tasks you'd give a personal assistant — scheduling meetings, managing your platform settings, connecting messaging channels, and completing browser-based tasks on your behalf. For example, when you want to schedule a weekly check-in, set up Telegram, or fill out an online form."
5
5
  model: claude-sonnet-4-6
6
- tools: mcp__admin__system-status, mcp__admin__brand-settings, mcp__admin__account-manage, mcp__admin__account-update, mcp__admin__logs-read, mcp__admin__plugin-read, mcp__admin__api-key-store, mcp__admin__api-key-verify, mcp__admin__render-component, mcp__admin__file-attach, mcp__admin__wifi, mcp__contacts__contact-create, mcp__contacts__contact-lookup, mcp__contacts__contact-update, mcp__contacts__contact-delete, mcp__contacts__contact-list, mcp__contacts__contact-export, mcp__contacts__contact-erase, mcp__contacts__group-create, mcp__contacts__group-manage, mcp__cloudflare__cf-set-token, mcp__cloudflare__cf-add-zone, mcp__cloudflare__cf-zone-status, mcp__cloudflare__tunnel-status, mcp__cloudflare__tunnel-install, mcp__cloudflare__tunnel-login, mcp__cloudflare__tunnel-create, mcp__cloudflare__tunnel-enable, mcp__cloudflare__tunnel-disable, mcp__cloudflare__tunnel-add-hostname, mcp__cloudflare__dns-lookup, mcp__telegram__message, mcp__telegram__message-history, mcp__telegram__telegram-webhook-register, mcp__whatsapp__whatsapp-login-start, mcp__whatsapp__whatsapp-login-wait, mcp__whatsapp__whatsapp-status, mcp__whatsapp__whatsapp-disconnect, mcp__whatsapp__whatsapp-send, mcp__whatsapp__whatsapp-send-document, mcp__whatsapp__whatsapp-config, mcp__whatsapp__whatsapp-activity, mcp__whatsapp__whatsapp-conversations, mcp__whatsapp__whatsapp-messages, mcp__whatsapp__whatsapp-group-info, mcp__email__email-setup, mcp__email__email-read, mcp__email__email-send, mcp__email__email-reply, mcp__email__email-search, mcp__email__email-graph-query, mcp__email__email-otp-extract, mcp__email__email-status, mcp__email__email-auto-respond-config, mcp__scheduling__schedule-event, mcp__scheduling__schedule-list, mcp__scheduling__schedule-get, mcp__scheduling__schedule-update, mcp__scheduling__schedule-cancel, mcp__scheduling__schedule-export-ics, mcp__scheduling__schedule-import-ics, mcp__scheduling__time-resolve, mcp__memory__memory-search, mcp__memory__profile-update, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_navigate_back, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_click, mcp__plugin_playwright_playwright__browser_fill, mcp__plugin_playwright_playwright__browser_fill_form, mcp__plugin_playwright_playwright__browser_type, mcp__plugin_playwright_playwright__browser_press_key, mcp__plugin_playwright_playwright__browser_hover, mcp__plugin_playwright_playwright__browser_select_option, mcp__plugin_playwright_playwright__browser_wait_for, mcp__plugin_playwright_playwright__browser_handle_dialog, mcp__plugin_playwright_playwright__browser_evaluate, mcp__plugin_playwright_playwright__browser_console_messages, mcp__plugin_playwright_playwright__browser_resize, mcp__plugin_playwright_playwright__browser_tabs, mcp__plugin_playwright_playwright__browser_close
6
+ tools: mcp__admin__system-status, mcp__admin__brand-settings, mcp__admin__account-manage, mcp__admin__account-update, mcp__admin__logs-read, mcp__admin__plugin-read, mcp__admin__api-key-store, mcp__admin__api-key-verify, mcp__admin__render-component, mcp__admin__file-attach, mcp__admin__wifi, mcp__contacts__contact-create, mcp__contacts__contact-lookup, mcp__contacts__contact-update, mcp__contacts__contact-delete, mcp__contacts__contact-list, mcp__contacts__contact-export, mcp__contacts__contact-erase, mcp__contacts__group-create, mcp__contacts__group-manage, mcp__cloudflare__cf-add-zone, mcp__cloudflare__cf-zone-status, mcp__cloudflare__cf-verify, mcp__cloudflare__cf-rebuild, mcp__cloudflare__tunnel-status, mcp__cloudflare__tunnel-install, mcp__cloudflare__tunnel-login, mcp__cloudflare__tunnel-create, mcp__cloudflare__tunnel-enable, mcp__cloudflare__tunnel-disable, mcp__cloudflare__tunnel-add-hostname, mcp__cloudflare__dns-lookup, mcp__telegram__message, mcp__telegram__message-history, mcp__telegram__telegram-webhook-register, mcp__whatsapp__whatsapp-login-start, mcp__whatsapp__whatsapp-login-wait, mcp__whatsapp__whatsapp-status, mcp__whatsapp__whatsapp-disconnect, mcp__whatsapp__whatsapp-send, mcp__whatsapp__whatsapp-send-document, mcp__whatsapp__whatsapp-config, mcp__whatsapp__whatsapp-activity, mcp__whatsapp__whatsapp-conversations, mcp__whatsapp__whatsapp-messages, mcp__whatsapp__whatsapp-group-info, mcp__email__email-setup, mcp__email__email-read, mcp__email__email-send, mcp__email__email-reply, mcp__email__email-search, mcp__email__email-graph-query, mcp__email__email-otp-extract, mcp__email__email-status, mcp__email__email-auto-respond-config, mcp__scheduling__schedule-event, mcp__scheduling__schedule-list, mcp__scheduling__schedule-get, mcp__scheduling__schedule-update, mcp__scheduling__schedule-cancel, mcp__scheduling__schedule-export-ics, mcp__scheduling__schedule-import-ics, mcp__scheduling__time-resolve, mcp__memory__memory-search, mcp__memory__profile-update, mcp__plugin_playwright_playwright__browser_navigate, mcp__plugin_playwright_playwright__browser_navigate_back, mcp__plugin_playwright_playwright__browser_snapshot, mcp__plugin_playwright_playwright__browser_click, mcp__plugin_playwright_playwright__browser_fill, mcp__plugin_playwright_playwright__browser_fill_form, mcp__plugin_playwright_playwright__browser_type, mcp__plugin_playwright_playwright__browser_press_key, mcp__plugin_playwright_playwright__browser_hover, mcp__plugin_playwright_playwright__browser_select_option, mcp__plugin_playwright_playwright__browser_wait_for, mcp__plugin_playwright_playwright__browser_handle_dialog, mcp__plugin_playwright_playwright__browser_evaluate, mcp__plugin_playwright_playwright__browser_console_messages, mcp__plugin_playwright_playwright__browser_resize, mcp__plugin_playwright_playwright__browser_tabs, mcp__plugin_playwright_playwright__browser_close
7
7
  ---
8
8
 
9
9
  # Personal Assistant
@@ -41,19 +41,23 @@ Manages events, appointments, and recurring triggers in the graph.
41
41
 
42
42
  ## Cloudflare Tunnel
43
43
 
44
- Guides setting up a Cloudflare Tunnel so the platform is reachable via a custom domain. All operations are deterministic MCP tool calls.
44
+ Guides setting up a Cloudflare Tunnel so the platform is reachable via a custom domain. The brand declares its Cloudflare zones at build time in `brand.json` (`cloudflare.zones`); every tool refuses operations against hostnames whose registrable parent is not in that list. Authentication is OAuth-only via `tunnel-login`; on first success the device records an account binding (`account-binding.json`) and refuses subsequent operations if the cert is later rotated under a different Cloudflare account.
45
45
 
46
- **Auth paths:** `tunnel-login` (one-click OAuth in VNC browser — preferred) or `cf-set-token` (user pastes an API token). Always check `tunnel-status` first to assess the current state. Both are idempotent.
46
+ **Auth path:** `tunnel-login` (one-click OAuth in VNC browser). Pass `force=true` to clear cert + binding when switching Cloudflare accounts. There is no API-token auth path the plugin recognises only the cert-bound account identity.
47
47
 
48
- **Setup flow:** Check status authenticate check/add zone update nameservers at registrar → create tunnel enable with remote password → verify URLs via browser. Not all steps are needed every time start from wherever the current state is.
48
+ **Setup flow:** `cloudflare-setup` is the single orchestrator it discovers state, prompts for tunnel/zone selection and labels via UI components, then creates the tunnel and routes DNS for the declared zones.
49
49
 
50
- **DNS:** `cf-add-zone` adds a domain to Cloudflare. `cf-zone-status` checks whether nameservers have propagated. Nameservers are the #1 troubleshooting issue. `dns-lookup` verifies DNS resolution for a hostname.
50
+ **Diagnostic flow:** `cf-verify` audits every relevant Cloudflare artefact and tags each as IN-SCOPE / OUT-OF-SCOPE / MISSING against the brand manifest and account binding. Non-mutating; safe to run at any time, including before login.
51
51
 
52
- **Tunnel lifecycle:** `tunnel-create` creates a tunnel + default hostname route. `tunnel-enable` starts the tunnel daemon with a remote management password. `tunnel-disable` stops it. `tunnel-add-hostname` adds additional routes for alias domains.
52
+ **Recovery flow:** `cf-rebuild` discards out-of-scope artefacts and reconstructs the declared state. Idempotent. Refuses to delete cert.pem when bound to the wrong account; halts with an actionable instruction to run `tunnel-login force=true`. Pass `dryRun=true` to preview.
53
53
 
54
- **User-facing language:** Say "connection" not "API token", "domain" not "zone", "domain routing" not "DNS". No Cloudflare internal terminology.
54
+ **DNS lookups:** `dns-lookup` for hostname resolution. Nameserver-not-yet-propagated is the most common operator issue; `cf-zone-status` reports activation status.
55
55
 
56
- **Verification:** Always verify URLs actually work by navigating to them in the browser. Never claim a URL works without browser verification.
56
+ **Alias domains:** `tunnel-add-hostname` registers an additional declared zone as an alias on an existing tunnel. The hostname must still be in scope per `brand.cloudflare.zones`.
57
+
58
+ **User-facing language:** Say "connection" not "certificate", "domain" not "zone", "address" not "hostname". No Cloudflare internal terminology.
59
+
60
+ **Verification:** Always verify URLs work by navigating to them in the browser. Never claim a URL works without browser verification.
57
61
 
58
62
  ## Telegram
59
63