@rubytech/create-realagent 1.0.852 → 1.0.854
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/__tests__/preflight-port-classifier.test.js +240 -73
- package/dist/index.js +59 -11
- package/dist/preflight-port-classifier.js +176 -41
- package/package.json +1 -1
- package/payload/platform/config/brand-registry.json +44 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts +21 -0
- package/payload/platform/lib/persistent-components/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/persistent-components/dist/index.js +32 -0
- package/payload/platform/lib/persistent-components/dist/index.js.map +1 -0
- package/payload/platform/lib/persistent-components/src/index.ts +28 -0
- package/payload/platform/lib/persistent-components/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/admin/PLUGIN.md +1 -1
- package/payload/platform/plugins/admin/hooks/__tests__/playwright-file-guard.test.sh +278 -0
- package/payload/platform/plugins/admin/hooks/playwright-file-guard.sh +204 -20
- package/payload/platform/plugins/admin/mcp/dist/index.js +40 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/deployment.md +2 -0
- package/payload/platform/plugins/docs/references/getting-started.md +2 -0
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +10 -0
- package/payload/platform/scripts/admin-persist-audit.ts +191 -0
- package/payload/platform/scripts/component-knowledgedoc-backfill.ts +214 -0
- package/payload/platform/scripts/installer-device-verify.sh +249 -0
- package/payload/platform/templates/specialists/agents/content-producer.md +2 -2
- package/payload/server/chunk-CFNSKDGA.js +667 -0
- package/payload/server/chunk-DC6DWYZJ.js +1603 -0
- package/payload/server/chunk-LTB5SSQW.js +10889 -0
- package/payload/server/chunk-MN2LGNUB.js +2143 -0
- package/payload/server/client-pool-AMT2W3II.js +34 -0
- package/payload/server/cloudflare-task-tracker-LJ4SMK2D.js +20 -0
- package/payload/server/maxy-edge.js +3 -3
- package/payload/server/public/assets/admin-DZ8Ke7t3.js +352 -0
- package/payload/server/public/assets/public-DApUXgoq.js +5 -0
- package/payload/server/public/assets/useVoiceRecorder-CI8GpxfU.js +36 -0
- package/payload/server/public/index.html +2 -2
- package/payload/server/public/public.html +2 -2
- package/payload/server/server.js +535 -351
- package/payload/server/public/assets/admin-Dyl8uNxX.js +0 -352
- package/payload/server/public/assets/public-B_PNZUph.js +0 -5
- package/payload/server/public/assets/useVoiceRecorder-fD0IWzJj.js +0 -36
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Task 939 — post-publish device-side verification harness.
|
|
3
|
+
#
|
|
4
|
+
# Closes the verification gap that let Task 938 archive on source-diff alone:
|
|
5
|
+
# without a device run, neither (a) the classifier-extension fix nor (b) the
|
|
6
|
+
# CDP probe brand-aware fix shipped to actual hardware. This script SSHes to
|
|
7
|
+
# every device in a manifest, runs the published installer for that brand,
|
|
8
|
+
# then greps the install log for the canonical CDP success banner.
|
|
9
|
+
#
|
|
10
|
+
# Exit zero ↔ every device's installer reached "Browser automation ready
|
|
11
|
+
# (CDP connected)". Any other state — ssh failure, npx non-zero, missing
|
|
12
|
+
# banner — is a non-zero exit with the failing device named on stderr.
|
|
13
|
+
#
|
|
14
|
+
# CONTRACT
|
|
15
|
+
# Archival of any task that touches packages/create-maxy/** is contingent
|
|
16
|
+
# on this script exiting zero against the published version. The exit code
|
|
17
|
+
# and the per-device summary are quoted in the close commit body.
|
|
18
|
+
#
|
|
19
|
+
# USAGE
|
|
20
|
+
# $ installer-device-verify.sh <published-version>
|
|
21
|
+
#
|
|
22
|
+
# <published-version> e.g. 1.0.853 — appended to `npx -y @rubytech/create-<brand>@<version>`.
|
|
23
|
+
#
|
|
24
|
+
# MANIFEST
|
|
25
|
+
# Path: $MAXY_VERIFY_MANIFEST (default $HOME/.maxy-verify-devices.json).
|
|
26
|
+
# Format: JSON array of devices; each entry:
|
|
27
|
+
# {
|
|
28
|
+
# "name": "maxy-pi-test", // free-form display name
|
|
29
|
+
# "brand": "maxy", // npm package suffix (@rubytech/create-<brand>)
|
|
30
|
+
# "configDir": ".maxy", // brand configDir; logs live in $HOME/<configDir>/logs/
|
|
31
|
+
# "sshTarget": "admin@maxytest.local", // user@host for ssh
|
|
32
|
+
# "sshPass": "password" // optional; uses sshpass if present
|
|
33
|
+
# }
|
|
34
|
+
# sshTarget supports inline port via "user@host:port" — script splits if present.
|
|
35
|
+
# sshPass omission → relies on existing ssh keys / agent.
|
|
36
|
+
#
|
|
37
|
+
# OUTPUT
|
|
38
|
+
# Per-device summary line on stdout.
|
|
39
|
+
# Detailed log: $HOME/.maxy/logs/installer-verify-<runId>.log (created if absent).
|
|
40
|
+
#
|
|
41
|
+
# DEPENDENCIES
|
|
42
|
+
# ssh (always), sshpass (only if any device uses sshPass), jq (always — the
|
|
43
|
+
# manifest is JSON; bash-only parse would be a CVE waiting to happen).
|
|
44
|
+
|
|
45
|
+
set -euo pipefail
|
|
46
|
+
|
|
47
|
+
if [[ $# -ne 1 ]]; then
|
|
48
|
+
echo "ERROR: missing argument <published-version>" >&2
|
|
49
|
+
echo "Usage: $0 <published-version>" >&2
|
|
50
|
+
echo "Example: $0 1.0.853" >&2
|
|
51
|
+
exit 2
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
VERSION="$1"
|
|
55
|
+
# Pattern-validate version before any remote-shell interpolation. The version
|
|
56
|
+
# flows into `npx -y @rubytech/create-<brand>@$VERSION` over SSH; a stray
|
|
57
|
+
# semicolon or backtick would land inside the remote shell. Operator-owned
|
|
58
|
+
# input but the principle is validate-at-boundary.
|
|
59
|
+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
|
60
|
+
echo "ERROR: invalid version '$VERSION' — expected semver (e.g. 1.0.853 or 1.0.853-rc.1)" >&2
|
|
61
|
+
exit 2
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
MANIFEST="${MAXY_VERIFY_MANIFEST:-$HOME/.maxy-verify-devices.json}"
|
|
65
|
+
RUN_ID="$(date +%Y%m%dT%H%M%SZ)-$$"
|
|
66
|
+
SUMMARY_DIR="$HOME/.maxy/logs"
|
|
67
|
+
SUMMARY_LOG="$SUMMARY_DIR/installer-verify-$RUN_ID.log"
|
|
68
|
+
|
|
69
|
+
if [[ ! -f "$MANIFEST" ]]; then
|
|
70
|
+
cat >&2 <<EOF
|
|
71
|
+
ERROR: device manifest not found at $MANIFEST.
|
|
72
|
+
|
|
73
|
+
Create one with this shape (JSON array, one entry per device):
|
|
74
|
+
[
|
|
75
|
+
{
|
|
76
|
+
"name": "maxy-pi-test",
|
|
77
|
+
"brand": "maxy",
|
|
78
|
+
"configDir": ".maxy",
|
|
79
|
+
"sshTarget": "admin@maxytest.local",
|
|
80
|
+
"sshPass": "password"
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
Or set \$MAXY_VERIFY_MANIFEST to point elsewhere.
|
|
85
|
+
Device list reference: ~/.claude/projects/-Users-neo-getmaxy/memory/reference_device_ssh.md
|
|
86
|
+
EOF
|
|
87
|
+
exit 2
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
91
|
+
echo "ERROR: jq is required (manifest is JSON)." >&2
|
|
92
|
+
echo " macOS: brew install jq" >&2
|
|
93
|
+
echo " Ubuntu: sudo apt-get install -y jq" >&2
|
|
94
|
+
exit 2
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
mkdir -p "$SUMMARY_DIR"
|
|
98
|
+
: > "$SUMMARY_LOG"
|
|
99
|
+
|
|
100
|
+
# Resolve sshpass once: required only if any manifest entry has sshPass set.
|
|
101
|
+
NEEDS_SSHPASS=$(jq -r 'any(.[]; .sshPass != null and .sshPass != "")' "$MANIFEST")
|
|
102
|
+
if [[ "$NEEDS_SSHPASS" == "true" ]] && ! command -v sshpass >/dev/null 2>&1; then
|
|
103
|
+
echo "ERROR: manifest references sshPass but sshpass is not installed." >&2
|
|
104
|
+
echo " macOS: brew install hudochenkov/sshpass/sshpass" >&2
|
|
105
|
+
echo " Ubuntu: sudo apt-get install -y sshpass" >&2
|
|
106
|
+
exit 2
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
DEVICE_COUNT=$(jq 'length' "$MANIFEST")
|
|
110
|
+
FAILED=0
|
|
111
|
+
|
|
112
|
+
# Common ssh hardening: short timeouts, no host-key prompt that would block
|
|
113
|
+
# when adding a new Pi to the fleet, and StrictHostKeyChecking=accept-new so
|
|
114
|
+
# fingerprints are recorded the first time but rejected on mismatch after.
|
|
115
|
+
SSH_OPTS=(
|
|
116
|
+
-o "ConnectTimeout=10"
|
|
117
|
+
-o "ServerAliveInterval=15"
|
|
118
|
+
-o "ServerAliveCountMax=2"
|
|
119
|
+
-o "StrictHostKeyChecking=accept-new"
|
|
120
|
+
-o "BatchMode=no"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
run_ssh() {
|
|
124
|
+
local target="$1" pass="$2"
|
|
125
|
+
shift 2
|
|
126
|
+
if [[ -n "$pass" ]]; then
|
|
127
|
+
SSHPASS="$pass" sshpass -e ssh "${SSH_OPTS[@]}" "$target" "$@"
|
|
128
|
+
else
|
|
129
|
+
ssh "${SSH_OPTS[@]}" "$target" "$@"
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Allowed brand pattern matches what bundle.js validates: lowercase
|
|
134
|
+
# alphanumeric, hyphens. Reject anything else before interpolating into
|
|
135
|
+
# remote shell commands. configDir mirrors the brand convention (a leading
|
|
136
|
+
# dot then the same character class), so the same shape passes through.
|
|
137
|
+
# `$NAME` is interpolated into `--hostname "$NAME"` over SSH — same boundary
|
|
138
|
+
# discipline applies; allow dots so it can carry mDNS hostnames like
|
|
139
|
+
# `maxytest.local`, but never quote characters or shell metacharacters.
|
|
140
|
+
ALLOWED='^[a-z0-9][a-z0-9-]*$'
|
|
141
|
+
ALLOWED_HOSTNAME='^[a-zA-Z0-9][a-zA-Z0-9.-]*$'
|
|
142
|
+
|
|
143
|
+
echo "installer-device-verify run=$RUN_ID version=$VERSION devices=$DEVICE_COUNT" | tee -a "$SUMMARY_LOG"
|
|
144
|
+
|
|
145
|
+
for i in $(seq 0 $((DEVICE_COUNT - 1))); do
|
|
146
|
+
NAME=$(jq -r ".[$i].name" "$MANIFEST")
|
|
147
|
+
BRAND=$(jq -r ".[$i].brand" "$MANIFEST")
|
|
148
|
+
CONFIGDIR=$(jq -r ".[$i].configDir" "$MANIFEST")
|
|
149
|
+
TARGET=$(jq -r ".[$i].sshTarget" "$MANIFEST")
|
|
150
|
+
PASS=$(jq -r ".[$i].sshPass // \"\"" "$MANIFEST")
|
|
151
|
+
|
|
152
|
+
if [[ ! "$BRAND" =~ $ALLOWED ]]; then
|
|
153
|
+
echo "FAIL $NAME — invalid brand '$BRAND' (must match $ALLOWED)" | tee -a "$SUMMARY_LOG"
|
|
154
|
+
FAILED=1
|
|
155
|
+
continue
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
if [[ ! "$NAME" =~ $ALLOWED_HOSTNAME ]]; then
|
|
159
|
+
echo "FAIL device name '$NAME' rejected — must match $ALLOWED_HOSTNAME" | tee -a "$SUMMARY_LOG"
|
|
160
|
+
FAILED=1
|
|
161
|
+
continue
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# configDir always begins with '.' (e.g. .maxy); strip the dot and validate
|
|
165
|
+
# the remainder with the same allowed-character class.
|
|
166
|
+
CD_TAIL="${CONFIGDIR#.}"
|
|
167
|
+
if [[ "$CD_TAIL" == "$CONFIGDIR" || ! "$CD_TAIL" =~ $ALLOWED ]]; then
|
|
168
|
+
echo "FAIL $NAME — invalid configDir '$CONFIGDIR' (must be a leading dot + $ALLOWED)" | tee -a "$SUMMARY_LOG"
|
|
169
|
+
FAILED=1
|
|
170
|
+
continue
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
echo "" | tee -a "$SUMMARY_LOG"
|
|
174
|
+
echo "[$NAME] brand=$BRAND target=$TARGET version=$VERSION" | tee -a "$SUMMARY_LOG"
|
|
175
|
+
|
|
176
|
+
# Step 1 — install. Run npx with auto-yes; redirect to the device's own
|
|
177
|
+
# install log path so the summary log on the operator side stays clean,
|
|
178
|
+
# and so the device-side log matches what reference_device_ssh.md would
|
|
179
|
+
# tail.
|
|
180
|
+
set +e
|
|
181
|
+
run_ssh "$TARGET" "$PASS" "npx -y @rubytech/create-$BRAND@$VERSION --hostname \"$NAME\"" >>"$SUMMARY_LOG" 2>&1
|
|
182
|
+
INSTALL_RC=$?
|
|
183
|
+
set -e
|
|
184
|
+
|
|
185
|
+
if [[ $INSTALL_RC -ne 0 ]]; then
|
|
186
|
+
echo "FAIL $NAME — install exited $INSTALL_RC (see $SUMMARY_LOG)" | tee -a "$SUMMARY_LOG"
|
|
187
|
+
FAILED=1
|
|
188
|
+
continue
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
# Step 2 — confirm a terminal-success banner in the latest install log.
|
|
192
|
+
# Pick the newest install-*.log by mtime (`ls -t`) and grep for either of
|
|
193
|
+
# the installer's two terminal-success markers emitted by
|
|
194
|
+
# packages/create-maxy/src/index.ts:
|
|
195
|
+
# • DISPLAY_MODE=virtual (Pi, headless VNC) →
|
|
196
|
+
# "Browser automation ready (CDP connected)" (index.ts:3012)
|
|
197
|
+
# • DISPLAY_MODE=native (laptop, on-demand Chromium) →
|
|
198
|
+
# "[cdp-check] skipped reason=native-display" (index.ts:3004)
|
|
199
|
+
# Both terminate the install successfully; either is a pass. Hard-coding
|
|
200
|
+
# only the virtual-mode banner would mark every laptop install FAIL even
|
|
201
|
+
# though the laptop install correctly skips CDP probing because there is
|
|
202
|
+
# no persistent Chromium running for CDP to talk to.
|
|
203
|
+
REMOTE_CHECK=$(cat <<'REMOTE'
|
|
204
|
+
set -euo pipefail
|
|
205
|
+
LOG_DIR="$HOME/__CONFIGDIR__/logs"
|
|
206
|
+
LATEST=$(ls -t "$LOG_DIR"/install-*.log 2>/dev/null | head -n1 || true)
|
|
207
|
+
if [[ -z "${LATEST:-}" ]]; then
|
|
208
|
+
echo "no-install-log"
|
|
209
|
+
exit 1
|
|
210
|
+
fi
|
|
211
|
+
echo "log=$LATEST"
|
|
212
|
+
if grep -F -q "Browser automation ready (CDP connected)" "$LATEST"; then
|
|
213
|
+
echo "marker=cdp-connected"
|
|
214
|
+
exit 0
|
|
215
|
+
fi
|
|
216
|
+
if grep -F -q "[cdp-check] skipped reason=native-display" "$LATEST"; then
|
|
217
|
+
echo "marker=native-display-skip"
|
|
218
|
+
exit 0
|
|
219
|
+
fi
|
|
220
|
+
echo "banner-not-found"
|
|
221
|
+
tail -n 30 "$LATEST"
|
|
222
|
+
exit 1
|
|
223
|
+
REMOTE
|
|
224
|
+
)
|
|
225
|
+
REMOTE_CHECK="${REMOTE_CHECK//__CONFIGDIR__/$CONFIGDIR}"
|
|
226
|
+
|
|
227
|
+
set +e
|
|
228
|
+
run_ssh "$TARGET" "$PASS" "$REMOTE_CHECK" >>"$SUMMARY_LOG" 2>&1
|
|
229
|
+
CHECK_RC=$?
|
|
230
|
+
set -e
|
|
231
|
+
|
|
232
|
+
if [[ $CHECK_RC -eq 0 ]]; then
|
|
233
|
+
echo "OK $NAME — CDP banner present" | tee -a "$SUMMARY_LOG"
|
|
234
|
+
else
|
|
235
|
+
echo "FAIL $NAME — CDP banner missing (rc=$CHECK_RC, see $SUMMARY_LOG)" | tee -a "$SUMMARY_LOG"
|
|
236
|
+
FAILED=1
|
|
237
|
+
fi
|
|
238
|
+
done
|
|
239
|
+
|
|
240
|
+
echo "" | tee -a "$SUMMARY_LOG"
|
|
241
|
+
if [[ $FAILED -eq 0 ]]; then
|
|
242
|
+
echo "PASS — all $DEVICE_COUNT devices installed and reported CDP banner" | tee -a "$SUMMARY_LOG"
|
|
243
|
+
echo "summary: $SUMMARY_LOG"
|
|
244
|
+
exit 0
|
|
245
|
+
else
|
|
246
|
+
echo "FAIL — at least one device did not reach CDP banner; details in $SUMMARY_LOG" | tee -a "$SUMMARY_LOG"
|
|
247
|
+
echo "summary: $SUMMARY_LOG"
|
|
248
|
+
exit 1
|
|
249
|
+
fi
|
|
@@ -64,9 +64,9 @@ Generate PDFs from rendered HTML pages using the browser tools.
|
|
|
64
64
|
- **Document structure:** Cover (full-bleed, print image fallback, `page-break-after: always`) > Optional TOC > Content (flowing) > Back page (mandatory for multi-page, `page-break-before: always`).
|
|
65
65
|
- **Single-pager:** Fixed `width: 210mm`, `min-height: 297mm`, `margin: 0 auto` on screen. In print add `height: 297mm`, `page-break-after: always`.
|
|
66
66
|
|
|
67
|
-
**Print image capture:** For cover and back page:
|
|
67
|
+
**Print image capture:** For cover and back page: navigate to the rendered HTML, set viewport to element size, hide UI chrome, take screenshot of element, save PNG. These images replace glassmorphism effects in print mode.
|
|
68
68
|
|
|
69
|
-
**Local files:** `file://` URLs are
|
|
69
|
+
**Local files:** `file://` URLs are rewritten transparently by the `playwright-file-guard` PreToolUse hook — pass them directly to `browser_navigate` and the hook will spawn a loopback `python3 -m http.server` on a free port and rewrite the URL before Playwright sees it. No agent-side server management needed.
|
|
70
70
|
|
|
71
71
|
## File delivery
|
|
72
72
|
|