@rubytech/create-maxy 1.0.856 → 1.0.859

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 (48) hide show
  1. package/dist/__tests__/cdp-port-no-silent-fallback.test.js +53 -0
  2. package/dist/index.js +58 -22
  3. package/dist/port-resolution.js +1 -1
  4. package/package.json +2 -2
  5. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +2 -2
  6. package/payload/platform/plugins/admin/mcp/dist/index.js +20 -10
  7. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  8. package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.js +1 -1
  9. package/payload/platform/plugins/admin/mcp/dist/lib/neo4j.js.map +1 -1
  10. package/payload/platform/plugins/cloudflare/references/manual-setup.md +1 -1
  11. package/payload/platform/plugins/cloudflare/scripts/_stream-log.sh +1 -1
  12. package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +36 -9
  13. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js +1 -1
  14. package/payload/platform/plugins/contacts/mcp/dist/lib/neo4j.js.map +1 -1
  15. package/payload/platform/plugins/email/mcp/dist/lib/neo4j.js +1 -1
  16. package/payload/platform/plugins/email/mcp/dist/lib/neo4j.js.map +1 -1
  17. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js +1 -1
  18. package/payload/platform/plugins/email/mcp/dist/scripts/email-auto-respond.js.map +1 -1
  19. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js +1 -1
  20. package/payload/platform/plugins/memory/mcp/dist/lib/neo4j.js.map +1 -1
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js +1 -1
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js.map +1 -1
  23. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +1 -1
  24. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  25. package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.js +1 -1
  26. package/payload/platform/plugins/tasks/mcp/dist/lib/neo4j.js.map +1 -1
  27. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js +1 -1
  28. package/payload/platform/plugins/telegram/mcp/dist/tools/message-history.js.map +1 -1
  29. package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.js +1 -1
  30. package/payload/platform/plugins/waitlist/mcp/dist/lib/neo4j.js.map +1 -1
  31. package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.js +1 -1
  32. package/payload/platform/plugins/workflows/mcp/dist/lib/neo4j.js.map +1 -1
  33. package/payload/platform/scripts/check-no-task-id-leaks.mjs +110 -0
  34. package/payload/platform/scripts/test-laptop-vnc-boot.sh +8 -1
  35. package/payload/platform/scripts/vnc.sh +40 -34
  36. package/payload/server/chunk-22LK7D5R.js +1612 -0
  37. package/payload/server/chunk-2Q2S52GB.js +10906 -0
  38. package/payload/server/chunk-7ADUQXTU.js +2143 -0
  39. package/payload/server/chunk-BY4LZDL4.js +667 -0
  40. package/payload/server/chunk-CNNPNADU.js +10891 -0
  41. package/payload/server/chunk-FL3H3AQD.js +1603 -0
  42. package/payload/server/client-pool-3BCJTPPA.js +34 -0
  43. package/payload/server/client-pool-WA5WGN7W.js +34 -0
  44. package/payload/server/cloudflare-task-tracker-OOQCL5ZB.js +20 -0
  45. package/payload/server/maxy-edge.js +34 -6
  46. package/payload/server/public/assets/{admin-CZpefPcA.js → admin-BumnnEDn.js} +60 -60
  47. package/payload/server/public/index.html +1 -1
  48. package/payload/server/server.js +202 -134
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+ // Build-time gate: rejects `(Task NNN)` citations in operator-visible production
3
+ // strings. Citations are allowed in inline comments and test titles/bodies only.
4
+ //
5
+ // Live repro that motivated the gate: setup-tunnel.sh on realagent 192.168.88.8
6
+ // emitted `Re-run the installer to provision Chromium (Task 929).` to the
7
+ // operator's chat surface — the citation made the operator chase Task 929's
8
+ // history rather than execute the verb. Operator surfaces strip citations;
9
+ // internal references keep them.
10
+ //
11
+ // Comment-skip is line-leading-character based, not AST. The repo's actual
12
+ // comment forms are: `#` (shell), `//` (TS/JS line), `*`/`/*`/`/**` (JSDoc
13
+ // continuation, block, doc), and `{/*` (JSX). Any line whose first non-
14
+ // whitespace token is one of these is skipped. Test files (`__tests__/` and
15
+ // `*.test.{ts,tsx,js}`) are skipped wholesale because the brief's out-of-scope
16
+ // rule covers test titles, and test bodies that assert on citations are
17
+ // equivalent to titles for this gate's purpose.
18
+
19
+ import { readFileSync, readdirSync, statSync } from 'node:fs'
20
+ import { dirname, join, relative, sep, basename } from 'node:path'
21
+ import { fileURLToPath } from 'node:url'
22
+
23
+ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url))
24
+ const REPO_ROOT = join(SCRIPT_DIR, '..', '..')
25
+
26
+ const SCAN_ROOTS = [
27
+ join(REPO_ROOT, 'platform', 'scripts'),
28
+ join(REPO_ROOT, 'platform', 'plugins'),
29
+ join(REPO_ROOT, 'platform', 'ui', 'server'),
30
+ join(REPO_ROOT, 'platform', 'ui', 'app'),
31
+ join(REPO_ROOT, 'packages', 'create-maxy', 'src'),
32
+ ]
33
+
34
+ const ALLOWED_EXTS = new Set(['.sh', '.ts', '.tsx', '.js', '.mjs', '.cjs'])
35
+ const SKIP_DIR_NAMES = new Set([
36
+ 'node_modules', 'dist', 'build', '.next', 'payload', '__tests__',
37
+ ])
38
+ const TASK_ID_RE = /\(Task \d+\)/
39
+
40
+ function isCommentLine(line) {
41
+ const trimmed = line.replace(/^\s+/, '')
42
+ if (trimmed.startsWith('#')) return true
43
+ if (trimmed.startsWith('//')) return true
44
+ if (trimmed.startsWith('/*')) return true
45
+ if (trimmed.startsWith('*')) return true
46
+ if (trimmed.startsWith('{/*')) return true
47
+ return false
48
+ }
49
+
50
+ function shouldScanFile(filePath) {
51
+ const name = basename(filePath)
52
+ if (name.endsWith('.test.ts')) return false
53
+ if (name.endsWith('.test.tsx')) return false
54
+ if (name.endsWith('.test.js')) return false
55
+ const ext = name.includes('.') ? '.' + name.split('.').pop() : ''
56
+ return ALLOWED_EXTS.has(ext)
57
+ }
58
+
59
+ function* walk(root) {
60
+ let entries
61
+ try {
62
+ entries = readdirSync(root)
63
+ } catch {
64
+ return
65
+ }
66
+ for (const name of entries) {
67
+ if (SKIP_DIR_NAMES.has(name)) continue
68
+ const full = join(root, name)
69
+ let st
70
+ try { st = statSync(full) } catch { continue }
71
+ if (st.isDirectory()) {
72
+ yield* walk(full)
73
+ } else if (st.isFile()) {
74
+ yield full
75
+ }
76
+ }
77
+ }
78
+
79
+ const leaks = []
80
+
81
+ for (const root of SCAN_ROOTS) {
82
+ for (const file of walk(root)) {
83
+ if (!shouldScanFile(file)) continue
84
+ let content
85
+ try {
86
+ content = readFileSync(file, 'utf-8')
87
+ } catch {
88
+ continue
89
+ }
90
+ const lines = content.split('\n')
91
+ for (let i = 0; i < lines.length; i++) {
92
+ const line = lines[i]
93
+ if (!TASK_ID_RE.test(line)) continue
94
+ if (isCommentLine(line)) continue
95
+ leaks.push({ file: relative(REPO_ROOT, file), line: i + 1, content: line.trim() })
96
+ }
97
+ }
98
+ }
99
+
100
+ if (leaks.length > 0) {
101
+ console.error(`check-no-task-id-leaks: ${leaks.length} operator-visible (Task NNN) citation(s) found:`)
102
+ for (const leak of leaks) {
103
+ console.error(` ${leak.file}:${leak.line}: ${leak.content}`)
104
+ }
105
+ console.error('')
106
+ console.error('Citations belong in inline comments only, never in operator-visible strings')
107
+ console.error('(stderr/stdout, log files surfaced via logs-read.sh, systemd unit')
108
+ console.error('--description=, error messages, console.log/warn/error, throw new Error).')
109
+ process.exit(1)
110
+ }
@@ -29,7 +29,14 @@ pass() { echo "OK: $*"; }
29
29
  [ -r "$BRAND_JSON" ] || fail "${BRAND_JSON} unreadable"
30
30
  command -v jq >/dev/null 2>&1 || fail "jq not on PATH (apt install jq)"
31
31
  CONFIG_DIR=$(jq -r '.configDir // ".maxy"' "$BRAND_JSON")
32
- CDP_PORT=$(jq -r '.cdpPort // 9222' "$BRAND_JSON")
32
+ # Task 959 — brand.json is the single source of truth for cdpPort at
33
+ # runtime; a missing field is a doctrinal violation, not a fallback.
34
+ if ! CDP_PORT=$(jq -er '.cdpPort' "$BRAND_JSON" 2>/dev/null); then
35
+ _keys=$(jq -r 'keys | join(",")' "$BRAND_JSON" 2>/dev/null || echo "unparseable")
36
+ _brand=$(echo "$CONFIG_DIR" | sed 's/^\.//')
37
+ echo "[test-laptop-vnc-boot] error reason=cdp-port-unresolved brand=$_brand path=$BRAND_JSON field=cdpPort json_keys=$_keys" >&2
38
+ exit 1
39
+ fi
33
40
  PERSIST_DIR="${HOME}/${CONFIG_DIR}"
34
41
  VNC_LOG="${PERSIST_DIR}/logs/vnc-boot.log"
35
42
 
@@ -41,36 +41,42 @@ set -uo pipefail
41
41
  SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
42
42
  PLATFORM_ROOT="${MAXY_PLATFORM_ROOT:-$(dirname "$SCRIPT_DIR")}"
43
43
  BRAND_JSON="${PLATFORM_ROOT}/config/brand.json"
44
- CONFIG_DIR=".maxy"
45
- VNC_DISPLAY_NUM=99
46
- RFB_PORT=""
47
- WEBSOCKIFY_PORT=""
48
- CDP_PORT=""
49
- BRAND_HOSTNAME="maxy"
50
- if [ -f "$BRAND_JSON" ] && command -v jq >/dev/null 2>&1; then
51
- _dir=$(jq -r '.configDir // empty' "$BRAND_JSON" 2>/dev/null) || true
52
- [ -n "$_dir" ] && CONFIG_DIR="$_dir"
53
- _vd=$(jq -r '.vncDisplay // empty' "$BRAND_JSON" 2>/dev/null) || true
54
- if [ -n "$_vd" ] && [ "$_vd" -eq "$_vd" ] 2>/dev/null; then
55
- VNC_DISPLAY_NUM="$_vd"
56
- fi
57
- _rfb=$(jq -r '.rfbPort // empty' "$BRAND_JSON" 2>/dev/null) || true
58
- if [ -n "$_rfb" ] && [ "$_rfb" -eq "$_rfb" ] 2>/dev/null; then RFB_PORT="$_rfb"; fi
59
- _wsp=$(jq -r '.websockifyPort // empty' "$BRAND_JSON" 2>/dev/null) || true
60
- if [ -n "$_wsp" ] && [ "$_wsp" -eq "$_wsp" ] 2>/dev/null; then WEBSOCKIFY_PORT="$_wsp"; fi
61
- _cdp=$(jq -r '.cdpPort // empty' "$BRAND_JSON" 2>/dev/null) || true
62
- if [ -n "$_cdp" ] && [ "$_cdp" -eq "$_cdp" ] 2>/dev/null; then CDP_PORT="$_cdp"; fi
63
- _hn=$(jq -r '.hostname // empty' "$BRAND_JSON" 2>/dev/null) || true
64
- [ -n "$_hn" ] && BRAND_HOSTNAME="$_hn"
44
+
45
+ # Task 959 — brand.json is the single source of truth at runtime; missing
46
+ # fields loud-fail rather than silently substituting a vncDisplay-derived
47
+ # offset (silent-fallback-masks-root-cause recurrence). The install-time
48
+ # offset rule in packages/create-maxy/src/index.ts stamps every brand at
49
+ # build time, so a correctly-installed device always has all five fields.
50
+ if [ ! -f "$BRAND_JSON" ]; then
51
+ echo "[vnc.sh] error reason=brand-config-missing path=$BRAND_JSON" >&2
52
+ exit 1
53
+ fi
54
+ if ! command -v jq >/dev/null 2>&1; then
55
+ echo "[vnc.sh] error reason=brand-config-missing path=$BRAND_JSON detail=\"jq not on PATH\"" >&2
56
+ exit 1
65
57
  fi
66
58
 
67
- # Task 924 deterministic fallback when brand.json omits any of the three
68
- # new port fields. Mirrors the rule in paths.ts so vnc.sh and the runtime
69
- # always agree on the port set even when only `vncDisplay` is stamped.
70
- _offset=$((VNC_DISPLAY_NUM - 99))
71
- [ -z "$RFB_PORT" ] && RFB_PORT=$((5900 + _offset))
72
- [ -z "$WEBSOCKIFY_PORT" ] && WEBSOCKIFY_PORT=$((6080 + _offset))
73
- [ -z "$CDP_PORT" ] && CDP_PORT=$((9222 + _offset))
59
+ # `exit 1` inside a `$(…)` command substitution only kills the subshell, so
60
+ # the loud-fail uses a parent-context `||` chain instead. Each field is read
61
+ # with `jq -e` (exit non-zero on null/missing) and a parent-context error
62
+ # helper invokes the script-level `exit 1`.
63
+ _fail_brand_field() {
64
+ local field="$1"
65
+ local keys
66
+ keys=$(jq -r 'keys | join(",")' "$BRAND_JSON" 2>/dev/null || echo "unparseable")
67
+ local brand_label
68
+ brand_label=$(jq -r '.configDir // "unknown"' "$BRAND_JSON" 2>/dev/null | sed 's/^\.//')
69
+ echo "[vnc.sh] error reason=cdp-port-unresolved brand=$brand_label path=$BRAND_JSON field=$field json_keys=$keys" >&2
70
+ exit 1
71
+ }
72
+
73
+ CONFIG_DIR=$(jq -er '.configDir' "$BRAND_JSON" 2>/dev/null) || _fail_brand_field configDir
74
+ VNC_DISPLAY_NUM=$(jq -er '.vncDisplay' "$BRAND_JSON" 2>/dev/null) || _fail_brand_field vncDisplay
75
+ RFB_PORT=$(jq -er '.rfbPort' "$BRAND_JSON" 2>/dev/null) || _fail_brand_field rfbPort
76
+ WEBSOCKIFY_PORT=$(jq -er '.websockifyPort' "$BRAND_JSON" 2>/dev/null) || _fail_brand_field websockifyPort
77
+ CDP_PORT=$(jq -er '.cdpPort' "$BRAND_JSON" 2>/dev/null) || _fail_brand_field cdpPort
78
+ BRAND_HOSTNAME=$(jq -r '.hostname // empty' "$BRAND_JSON" 2>/dev/null)
79
+ [ -z "$BRAND_HOSTNAME" ] && BRAND_HOSTNAME="${CONFIG_DIR#.}"
74
80
 
75
81
  VNC_DISPLAY=":${VNC_DISPLAY_NUM}"
76
82
  MAXY_DIR="${HOME}/${CONFIG_DIR}"
@@ -96,18 +102,18 @@ CHROMIUM_BIN_FILE="${PLATFORM_ROOT}/config/chromium-binary.path"
96
102
  if [ ! -r "$CHROMIUM_BIN_FILE" ]; then
97
103
  log "[vnc.sh:chromium] FATAL: ${CHROMIUM_BIN_FILE} missing or unreadable"
98
104
  echo "ERROR: ${CHROMIUM_BIN_FILE} missing or unreadable." >&2
99
- echo " Re-run the installer to provision a non-snap Chromium (Task 929)." >&2
105
+ echo " Re-run the installer to provision a non-snap Chromium." >&2
100
106
  exit 1
101
107
  fi
102
108
  CHROMIUM_BIN="$(head -n1 "$CHROMIUM_BIN_FILE" | tr -d '[:space:]')"
103
109
  if [ -z "$CHROMIUM_BIN" ]; then
104
110
  log "[vnc.sh:chromium] FATAL: ${CHROMIUM_BIN_FILE} is empty"
105
- echo "ERROR: ${CHROMIUM_BIN_FILE} is empty — re-run installer (Task 929)." >&2
111
+ echo "ERROR: ${CHROMIUM_BIN_FILE} is empty — re-run installer." >&2
106
112
  exit 1
107
113
  fi
108
114
  if [ ! -x "$CHROMIUM_BIN" ]; then
109
115
  log "[vnc.sh:chromium] FATAL: configured CHROMIUM_BIN=${CHROMIUM_BIN} is not executable"
110
- echo "ERROR: configured Chromium binary ${CHROMIUM_BIN} is not executable — re-run installer (Task 929)." >&2
116
+ echo "ERROR: configured Chromium binary ${CHROMIUM_BIN} is not executable — re-run installer." >&2
111
117
  exit 1
112
118
  fi
113
119
  CHROMIUM_REALPATH="$(readlink -f "$CHROMIUM_BIN" 2>/dev/null || echo "$CHROMIUM_BIN")"
@@ -115,7 +121,7 @@ case ":$(echo "$CHROMIUM_REALPATH" | tr '/' ':'):" in
115
121
  *:snap:*)
116
122
  log "[vnc.sh:chromium] FATAL: CHROMIUM_BIN=${CHROMIUM_BIN} resolves to ${CHROMIUM_REALPATH} (snap-confined)"
117
123
  echo "ERROR: configured Chromium ${CHROMIUM_BIN} resolves to ${CHROMIUM_REALPATH} which is snap-confined." >&2
118
- echo " Snap AppArmor denies writes to ~/.{brand}/chromium-profile/. Re-run installer to install Google Chrome (Task 929)." >&2
124
+ echo " Snap AppArmor denies writes to ~/.{brand}/chromium-profile/. Re-run installer to install Google Chrome." >&2
119
125
  exit 1
120
126
  ;;
121
127
  esac
@@ -305,7 +311,7 @@ start_chrome_on() {
305
311
  if check_window_on_display "${target_display}"; then
306
312
  log "Chromium ready (${label}) with CDP on :${CDP_PORT} windowPresent=true"
307
313
  else
308
- log "WARNING: Chromium CDP up on :${CDP_PORT} but no window visible on ${target_display} (${label}) — observability-only, not gating (Task 632)"
314
+ log "WARNING: Chromium CDP up on :${CDP_PORT} but no window visible on ${target_display} (${label}) — observability-only, not gating"
309
315
  fi
310
316
  else
311
317
  log "ERROR: Chromium failed to start on ${target_display} (${label}) — CDP port ${CDP_PORT} not listening (browser-specialist degraded)"
@@ -381,7 +387,7 @@ start_chrome_native() {
381
387
  elif [ "$NATIVE_SESSION_TYPE" = "wayland" ]; then
382
388
  log "Chromium ready (native) with CDP on :${CDP_PORT} windowPresent=unknown (wayland)"
383
389
  else
384
- log "WARNING: Chromium CDP up on :${CDP_PORT} but no window visible on ${NATIVE_DISPLAY} (native) — observability-only, not gating (Task 632)"
390
+ log "WARNING: Chromium CDP up on :${CDP_PORT} but no window visible on ${NATIVE_DISPLAY} (native) — observability-only, not gating"
385
391
  fi
386
392
  else
387
393
  log "ERROR: Chromium failed to start on ${NATIVE_DISPLAY} (native) — CDP port ${CDP_PORT} not listening (browser-specialist degraded)"