@rubytech/create-realagent 1.0.663 → 1.0.665
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 +5 -1
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +34 -1
- package/payload/platform/plugins/docs/references/memory-guide.md +2 -2
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +24 -6
- package/payload/platform/scripts/vnc.sh +174 -2
- package/payload/server/public/assets/{admin-C9qoVb2l.js → admin-Brug36E-.js} +5 -5
- package/payload/server/public/assets/data-woLf2Tmp.js +1 -0
- package/payload/server/public/assets/{file-lmzx24EO.js → file-rN5uuyaV.js} +1 -1
- package/payload/server/public/assets/{graph-DkjvCb8B.js → graph-BYaOEZUg.js} +16 -16
- package/payload/server/public/assets/{house-ClhI06TA.js → house-DnFgpCt2.js} +1 -1
- package/payload/server/public/assets/jsx-runtime-CSCPZpLN.css +1 -0
- package/payload/server/public/assets/{public-Dz33-dIE.js → public-BRrqpeVH.js} +1 -1
- package/payload/server/public/assets/{share-2-MZ4MqbjS.js → share-2-DLjRUEiG.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-Cm0G51D_.js → useVoiceRecorder-D_efR3Nx.js} +1 -1
- package/payload/server/public/assets/{x-CLhtM_Mh.js → x-L6KPMfIN.js} +1 -1
- package/payload/server/public/data.html +6 -6
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +7 -7
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +360 -126
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.test.d.ts +0 -2
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.test.d.ts.map +0 -1
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.test.js +0 -224
- package/payload/platform/plugins/admin/mcp/dist/lib/onboarding.test.js.map +0 -1
- package/payload/platform/plugins/documents/mcp/dist/index.d.ts +0 -2
- package/payload/platform/plugins/documents/mcp/dist/index.d.ts.map +0 -1
- package/payload/platform/plugins/documents/mcp/dist/index.js +0 -98
- package/payload/platform/plugins/documents/mcp/dist/index.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.test.d.ts +0 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.test.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.test.js +0 -233
- package/payload/platform/plugins/memory/mcp/dist/lib/semantic-chunker.test.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.d.ts +0 -18
- package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.js +0 -80
- package/payload/platform/plugins/memory/mcp/dist/scripts/graph-prune.js.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.d.ts +0 -7
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.d.ts.map +0 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.js +0 -10
- package/payload/platform/plugins/memory/mcp/dist/tools/graph-prune-run.js.map +0 -1
- package/payload/platform/plugins/waitlist/mcp/dist/lib/anthropic.d.ts +0 -23
- package/payload/platform/plugins/waitlist/mcp/dist/lib/anthropic.d.ts.map +0 -1
- package/payload/platform/plugins/waitlist/mcp/dist/lib/anthropic.js +0 -115
- package/payload/platform/plugins/waitlist/mcp/dist/lib/anthropic.js.map +0 -1
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-extract.d.ts +0 -12
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-extract.d.ts.map +0 -1
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-extract.js +0 -197
- package/payload/platform/plugins/waitlist/mcp/dist/tools/waitlist-extract.js.map +0 -1
- package/payload/server/public/assets/data-C-WE3FGr.js +0 -1
- package/payload/server/public/assets/jsx-runtime-CLCFnMYD.css +0 -1
- /package/payload/server/public/assets/{jsx-runtime-ImbU973I.js → jsx-runtime-XWiDQoTG.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -219,7 +219,11 @@ function installSystemDeps() {
|
|
|
219
219
|
if (canSudo()) {
|
|
220
220
|
shell("apt-get", ["update"], { sudo: true });
|
|
221
221
|
shell("apt-get", ["install", "-y", "curl", "git", "unzip", "jq", "avahi-daemon", "avahi-utils", "poppler-utils", "ffmpeg"], { sudo: true });
|
|
222
|
-
|
|
222
|
+
// xterm is the fallback terminal-emulator binary for the VNC-rendered
|
|
223
|
+
// Terminal surface (Task 627). Always installed to guarantee vnc.sh
|
|
224
|
+
// start-terminal has a binary to spawn on Bookworm Pis where
|
|
225
|
+
// gnome-terminal is not preinstalled (avoids pulling ~180MB of GNOME).
|
|
226
|
+
shell("apt-get", ["install", "-y", "tigervnc-standalone-server", "python3-websockify", "novnc", "xdg-utils", "chromium", "xterm"], { sudo: true });
|
|
223
227
|
shell("apt-get", ["install", "-y", "hostapd", "dnsmasq"], { sudo: true });
|
|
224
228
|
// tmux backs the admin terminal's persistent named session (Task 591).
|
|
225
229
|
// ttyd is NOT in Debian Bookworm's apt repo (Task 602) — it ships as a
|
package/package.json
CHANGED
|
@@ -263,6 +263,13 @@ ON EACH [k.summary, k.content];
|
|
|
263
263
|
// (Conversation)-[:BELONGS_TO]->(LocalBusiness).
|
|
264
264
|
// Public conversations carry a visitorId (browser cookie) and
|
|
265
265
|
// agentSlug for session resume across page reloads and server restarts.
|
|
266
|
+
// Task 621 — carries a `summary` string denormalised from the first
|
|
267
|
+
// user-role message (first 200 chars, set once, no overwrites) to
|
|
268
|
+
// provide a stable human-identifiable label on the /graph canvas.
|
|
269
|
+
// Conversations that never receive a user message (assistant-only
|
|
270
|
+
// seed, etc.) leave `summary` NULL; the /graph client's pickShortLabel
|
|
271
|
+
// falls back to `Conv <shortId>` so empty conversations stay
|
|
272
|
+
// distinguishable from each other.
|
|
266
273
|
// ----------------------------------------------------------
|
|
267
274
|
|
|
268
275
|
CREATE CONSTRAINT conversation_id_unique IF NOT EXISTS
|
|
@@ -297,7 +304,13 @@ OPTIONS {
|
|
|
297
304
|
|
|
298
305
|
// ----------------------------------------------------------
|
|
299
306
|
// Message node — individual messages within a Conversation
|
|
300
|
-
// Linked via (Message)-[:PART_OF]->(Conversation).
|
|
307
|
+
// Linked via (Message)-[:PART_OF]->(Conversation) to the parent.
|
|
308
|
+
// Linked via (Message)-[:NEXT]->(Message) to the next message in
|
|
309
|
+
// insertion order (Task 621). The chain is linear — one outgoing
|
|
310
|
+
// NEXT per message — so a Conversation with N messages has N-1
|
|
311
|
+
// NEXT edges. Concurrent writes against the same Conversation
|
|
312
|
+
// CAN fork the chain under Neo4j's READ_COMMITTED default; tracked
|
|
313
|
+
// under Task 624.
|
|
301
314
|
// Vector-indexed for semantic search over conversation history.
|
|
302
315
|
// ----------------------------------------------------------
|
|
303
316
|
|
|
@@ -615,6 +628,26 @@ FOR (au:AdminUser) REQUIRE au.userId IS UNIQUE;
|
|
|
615
628
|
CREATE INDEX graph_preference_account_user IF NOT EXISTS
|
|
616
629
|
FOR (p:GraphPreference) ON (p.accountId, p.userId);
|
|
617
630
|
|
|
631
|
+
// ----------------------------------------------------------
|
|
632
|
+
// ReviewAlert — review-detector rule-match aggregation
|
|
633
|
+
//
|
|
634
|
+
// One alert per (accountId, ruleId); subsequent rule matches bump
|
|
635
|
+
// lastMatchAt and cumulativeMatchCount via ON MATCH. Written by
|
|
636
|
+
// platform/ui/app/lib/review-detector/writer.ts, read by the admin
|
|
637
|
+
// chat's alert-surfacing tools. Listed in FILTER_EXCLUDED_LABELS
|
|
638
|
+
// (graph-labels.ts, Task 626) so it never surfaces as a /graph filter
|
|
639
|
+
// row, but registered in GRAPH_LABEL_COLOURS so neighbourhood-mode
|
|
640
|
+
// drilldown / search hits can still render it.
|
|
641
|
+
//
|
|
642
|
+
// Composite MERGE key (accountId, ruleId) — no UNIQUE constraint for
|
|
643
|
+
// the same reason as GraphPreference: the composite MERGE is
|
|
644
|
+
// idempotent and a composite constraint would add schema surface
|
|
645
|
+
// for no behavioural gain.
|
|
646
|
+
// ----------------------------------------------------------
|
|
647
|
+
|
|
648
|
+
CREATE INDEX review_alert_account_rule IF NOT EXISTS
|
|
649
|
+
FOR (a:ReviewAlert) ON (a.accountId, a.ruleId);
|
|
650
|
+
|
|
618
651
|
// ----------------------------------------------------------
|
|
619
652
|
// ToolCall — durable audit trail for agent tool invocations
|
|
620
653
|
//
|
|
@@ -84,9 +84,9 @@ Ask naturally:
|
|
|
84
84
|
|
|
85
85
|
Maxy answers relational questions — "list all my people", "how many tasks do I have", "find the person with email X", "show me the 20 most recently created nodes" — via direct read-only Cypher against your Neo4j. This is faster and more precise than semantic search when the question is "the exact set where", not "things similar to".
|
|
86
86
|
|
|
87
|
-
You can also open a visual view of your graph at any time from the burger menu → **Graph**. Click the **Filter** button in the toolbar to open the filter menu — it lists only the node types that actually exist in your graph
|
|
87
|
+
You can also open a visual view of your graph at any time from the burger menu → **Graph**. Click the **Filter** button in the toolbar to open the filter menu — it lists only the node types that actually exist in your graph, one row per type, showing your per-type node count and sorted so the most-connected types sit at the top. Leaf types that only appear inside a parent (messages inside a conversation, sections inside a document) are never filter rows — you reach them by clicking the parent and exploring its neighbourhood. Infrastructural types (`ToolCall`, `WorkflowRun`, `WorkflowStep`, `ReviewAlert`) never appear as filter rows — the first three are execution plumbing, and `ReviewAlert` has its own dedicated surface in admin chat. Active rows render a force-directed map, coloured by label (Person, Service, KnowledgeDocument, Task, …). Click a node to see its properties and explore its neighbourhood; click the **Back** control or empty canvas to return to your filter view with the same rows still selected. Type in the search box to highlight matches; submitting a search also widens the filter to include any node types the hits belong to, so relevant matches render instead of disappearing into a "not in current view" banner. The **×** buttons on the search box and inside the filter menu clear the current search and the current selection respectively — clearing the filter selection does not touch your saved default.
|
|
88
88
|
|
|
89
|
-
**Save a default view:** once you have the
|
|
89
|
+
**Save a default view:** once you have the rows you want, click **Set default view** in the filter menu. Next time you open **Graph**, those rows are pre-selected and your data renders immediately. The default is per-admin, per-account — each admin on each account has their own.
|
|
90
90
|
|
|
91
91
|
**Delete a node:** drag it to the trash icon top-right of the canvas. No confirmation — deletes are reversible for 30 days. To restore, toggle **Show trashed** inside the filter menu and click **Restore** on the node, or ask Maxy in chat ("restore the <label> I just deleted").
|
|
92
92
|
|
|
@@ -72,7 +72,7 @@ The Software Update window mounts the terminal lazily: neither the terminal, its
|
|
|
72
72
|
|
|
73
73
|
Because the terminal is the only surface, it narrates its own state when something goes wrong. The moment you click Upgrade, the terminal echoes a timestamped `[upgrade] starting at <UTC> — shell+ws+tmux+xterm chain OK` line before the `npx` invocation begins — that single line confirms the WebSocket, tmux session, shell, and xterm renderer are all working end-to-end. If 5 seconds then pass with no output from `npx`, the terminal itself writes a `[terminal] no bytes from upstream in 5s — ws.readyState=…, bytesReceived=…, attempt=…` diagnostic into its own buffer, and repeats that narration every 30 seconds of continued silence. If even that narration never appears within ~10 seconds of click, the xterm renderer or its WebSocket is broken and reloading the admin UI is the fix. On the server side, `~/.maxy/logs/terminal.log` carries a `terminal-proxy-flow` heartbeat every 5 seconds while bytes are moving and every ~30 seconds while idle, with `clientBytes`, `upstreamBytes`, and `idleMs` fields — so a stuck upstream presents as `upstreamBytes` frozen and `idleMs` climbing, directly visible in a `tail -f` without any client-side evidence needed.
|
|
74
74
|
|
|
75
|
-
The
|
|
75
|
+
The burger menu in the admin chat also includes a standalone **Terminal** entry next to **Browser**, but this surface is intentionally *different* from the upgrade-modal terminal. Clicking it spawns a real GUI terminal emulator (gnome-terminal on desktops that ship it, xterm elsewhere) on the VNC virtual display `:99` via the same `/vnc-viewer.html` iframe that Browser uses. The header Terminal is isomorphic to the header Browser: one pipeline (launch endpoint → display spawn → noVNC iframe), one failure domain — and deliberately decoupled from ttyd. Stopping `maxy-ttyd.service` leaves the header Terminal fully functional; only the in-modal upgrade terminal degrades. Closing the header Terminal overlay (Escape or the `×` button) kills the spawned emulator PID on the Pi via `POST /api/admin/terminal/close`; re-opening launches a fresh shell. Authorisation is inherited from the same `canAccessAdmin()` gate that wraps every `/api/admin/*` route. The launch endpoint logs to `~/.maxy/logs/terminal-launch.log` (script-level spawn/kill events) and to `vnc-boot.log` via `vncLog('ensure-terminal', ...)` (Node-side state machine), mirroring the Browser's `ensure-cdp` observability shape.
|
|
76
76
|
|
|
77
77
|
## AI Content Provenance
|
|
78
78
|
|
|
@@ -142,18 +142,36 @@ In `terminal.log`, look for `terminal-proxy-flow` lines — they're emitted ever
|
|
|
142
142
|
|
|
143
143
|
**If no narration appears at all within ~10 seconds of click:** the xterm renderer or its WebSocket is broken — reload the admin UI in your browser. The absence of the self-narration is itself the diagnostic. On reload, if the preamble line does not appear again within a few hundred milliseconds of clicking Upgrade, the admin server cannot reach `ttyd`; follow the "Admin terminal not available" steps above.
|
|
144
144
|
|
|
145
|
-
## Terminal
|
|
145
|
+
## Header Terminal click shows an error alert
|
|
146
146
|
|
|
147
|
-
**Symptom:** You opened the burger menu
|
|
147
|
+
**Symptom:** You opened the burger menu and clicked **Terminal**, but instead of the overlay appearing you got an inline error like "Terminal failed to start" or "VNC failed to start".
|
|
148
148
|
|
|
149
|
-
**What it means:** `
|
|
149
|
+
**What it means:** `POST /api/admin/terminal/launch` returned a 502 — either the VNC stack on port 5900 is down or the terminal emulator could not be spawned on display `:99`. The header Terminal is decoupled from `ttyd`; this is a VNC-stack or display-spawn failure, not an upgrade-terminal problem.
|
|
150
|
+
|
|
151
|
+
Step-by-step diagnosis:
|
|
150
152
|
|
|
151
153
|
```bash
|
|
152
|
-
|
|
153
|
-
sudo tail -n 50 ~/.maxy/logs/terminal.log
|
|
154
|
+
# 1. Check the terminal-launch log for the specific failure reason
|
|
155
|
+
sudo tail -n 50 ~/.maxy/logs/terminal-launch.log
|
|
156
|
+
|
|
157
|
+
# 2. Check the node-side state machine (ensure-terminal entries)
|
|
158
|
+
sudo grep ensure-terminal ~/.maxy/logs/vnc-boot.log | tail -20
|
|
159
|
+
|
|
160
|
+
# 3. Verify the VNC display itself is healthy
|
|
161
|
+
sudo ~/maxy/platform/scripts/vnc.sh status # should print "running"
|
|
162
|
+
DISPLAY=:99 xdpyinfo >/dev/null 2>&1 && echo "display ok" || echo "display dead"
|
|
163
|
+
|
|
164
|
+
# 4. Confirm a terminal binary is installed (xterm is the always-available fallback)
|
|
165
|
+
which gnome-terminal; which xterm
|
|
154
166
|
```
|
|
155
167
|
|
|
156
|
-
|
|
168
|
+
Most common failures and fixes:
|
|
169
|
+
|
|
170
|
+
- `[terminal-launch] failed err="no terminal emulator installed"` → run `sudo apt-get install -y xterm` (or re-run the installer, which installs xterm as a dependency).
|
|
171
|
+
- `[terminal-launch] failed err="spawn detached but no terminal PID visible within 1s"` → X server on `:99` is wedged. `sudo systemctl --user restart maxy-ui` cycles the VNC stack via `vnc.sh start`.
|
|
172
|
+
- `ensure-terminal action="escalate-vnc-restart"` followed by `degraded` → `Xtigervnc` itself is not coming up. Check `~/.maxy/logs/vnc-boot.log` for the tigervnc startup lines.
|
|
173
|
+
|
|
174
|
+
The header Terminal is decoupled from `maxy-ttyd.service` by design — stopping that service should *not* break the header Terminal. If the upgrade modal breaks but the header Terminal still works, the problem is isolated to ttyd (see the "Admin Terminal Stuck Disconnected After Upgrade" and "Admin terminal not available" sections above).
|
|
157
175
|
|
|
158
176
|
## Orphan Account Directory Archived to `.trash/`
|
|
159
177
|
|
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
# VNC + browser lifecycle — single source of truth.
|
|
3
3
|
# Called by systemd ExecStartPre (boot) and lib/vnc.ts ensureVnc() (recovery).
|
|
4
4
|
#
|
|
5
|
-
# Usage: vnc.sh start | stop | start-chrome | start-chrome-native
|
|
5
|
+
# Usage: vnc.sh start | stop | start-chrome | start-chrome-native
|
|
6
|
+
# | start-terminal | start-terminal-native | status
|
|
6
7
|
#
|
|
7
8
|
# Components:
|
|
8
9
|
# Xtigervnc :99 — virtual X11 display + VNC server on port 5900
|
|
9
10
|
# websockify :6080 — WebSocket bridge serving noVNC static files
|
|
10
11
|
# Chromium :9222 — headed browser with CDP enabled
|
|
11
12
|
# (Playwright MCP connects via --cdp-endpoint)
|
|
13
|
+
# Terminal emulator — gnome-terminal or xterm, spawned on demand for the
|
|
14
|
+
# admin Terminal overlay (Task 627). Isomorphic to the
|
|
15
|
+
# Chromium pipeline — same :99 VNC display, same
|
|
16
|
+
# /vnc-viewer.html iframe surface.
|
|
12
17
|
#
|
|
13
18
|
# Display modes (DISPLAY_MODE env var, set by installer --display flag):
|
|
14
19
|
# virtual (default) — Chromium runs on :99 (VNC virtual display)
|
|
@@ -33,13 +38,19 @@ fi
|
|
|
33
38
|
MAXY_DIR="${HOME}/${CONFIG_DIR}"
|
|
34
39
|
LOG_DIR="${MAXY_DIR}/logs"
|
|
35
40
|
LOG_FILE="${LOG_DIR}/vnc-boot.log"
|
|
41
|
+
TERMINAL_LOG="${LOG_DIR}/terminal-launch.log"
|
|
36
42
|
|
|
37
43
|
mkdir -p "$LOG_DIR"
|
|
38
44
|
|
|
39
45
|
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }
|
|
46
|
+
tlog() { echo "[$(date '+%Y-%m-%dT%H:%M:%S%z')] [terminal-launch] $*" >> "$TERMINAL_LOG"; }
|
|
40
47
|
|
|
41
48
|
kill_stale() {
|
|
42
49
|
pkill -f 'chromium.*remote-debugging-port=9222' 2>/dev/null || true
|
|
50
|
+
# Terminal emulators launched by start-terminal[-native] (Task 627).
|
|
51
|
+
# Regex is anchored to avoid killing gnome-terminal-server (D-Bus service
|
|
52
|
+
# always running on GNOME desktops — not ours to manage).
|
|
53
|
+
kill_terminal_emulators
|
|
43
54
|
pkill -f 'Xtigervnc :99' 2>/dev/null || true
|
|
44
55
|
pkill -f 'websockify.*6080' 2>/dev/null || true
|
|
45
56
|
rm -f /tmp/.X99-lock /tmp/.X11-unix/X99
|
|
@@ -170,6 +181,143 @@ start_chrome() {
|
|
|
170
181
|
start_chrome_on ":99" "vnc"
|
|
171
182
|
}
|
|
172
183
|
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
# Terminal emulator lifecycle (Task 627) — isomorphic to Chromium's.
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
# Resolve the preferred terminal binary + its required flags. Prefers
|
|
189
|
+
# gnome-terminal on desktops that ship it (Ubuntu), falls back to xterm
|
|
190
|
+
# (Bookworm minimum). Prints "<bin>\t<flags>" on stdout (tab-separated), or
|
|
191
|
+
# exits non-zero with a loud-fail log if neither is installed — matches the
|
|
192
|
+
# operator invariant "no ttyd fallback, no silent substitution" from Task 627.
|
|
193
|
+
#
|
|
194
|
+
# gnome-terminal needs `--wait` because its /usr/bin/gnome-terminal entry is a
|
|
195
|
+
# python D-Bus launcher that forks `/usr/bin/gnome-terminal.real` and would
|
|
196
|
+
# otherwise exit seconds after dispatch — leaving our pgrep-based liveness
|
|
197
|
+
# probe blind while the actual window (owned by gnome-terminal-server) is
|
|
198
|
+
# still on screen. With --wait, both the python wrapper and .real stay alive
|
|
199
|
+
# for the duration of the shell session. xterm is a single-process emulator
|
|
200
|
+
# and needs no flag.
|
|
201
|
+
resolve_terminal_bin() {
|
|
202
|
+
if [ -x /usr/bin/gnome-terminal ]; then
|
|
203
|
+
printf '%s\t%s\n' '/usr/bin/gnome-terminal' '--wait'
|
|
204
|
+
return 0
|
|
205
|
+
fi
|
|
206
|
+
if [ -x /usr/bin/xterm ]; then
|
|
207
|
+
printf '%s\t%s\n' '/usr/bin/xterm' ''
|
|
208
|
+
return 0
|
|
209
|
+
fi
|
|
210
|
+
tlog "failed err=\"no terminal emulator installed (expected /usr/bin/gnome-terminal or /usr/bin/xterm)\""
|
|
211
|
+
log "ERROR: no terminal emulator binary found — install xterm (apt-get install -y xterm)"
|
|
212
|
+
return 1
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# pgrep pattern that matches operator-launched terminals but NEVER matches
|
|
216
|
+
# /usr/libexec/gnome-terminal-server (D-Bus service, pre-existing). Matches:
|
|
217
|
+
# - /usr/bin/gnome-terminal --wait (Ubuntu's python wrapper, invoked with --wait)
|
|
218
|
+
# - /usr/bin/python3 /usr/bin/gnome-terminal (wrapper as seen via pgrep -f)
|
|
219
|
+
# - /usr/bin/gnome-terminal.real --wait (actual binary, child of wrapper)
|
|
220
|
+
# - /usr/bin/xterm, xterm -geometry ... (xterm, single-process emulator)
|
|
221
|
+
# Rejects:
|
|
222
|
+
# - /usr/libexec/gnome-terminal-server (D-Bus service, not ours to manage)
|
|
223
|
+
#
|
|
224
|
+
# The `(^|/)` alternation anchors on either the start of the cmdline or a
|
|
225
|
+
# preceding `/` path separator, so the python wrapper path `/usr/bin/python3
|
|
226
|
+
# /usr/bin/gnome-terminal --wait` is matched via the inner `/gnome-terminal`.
|
|
227
|
+
# The `(\.real)?` optional suffix catches the actual binary. The trailing
|
|
228
|
+
# `([[:space:]]|$)` breaks the match on `-server` (dash is not whitespace).
|
|
229
|
+
# POSIX ERE — procps `pgrep` does not support `\s`, so use [[:space:]].
|
|
230
|
+
_terminal_pgrep_pattern() {
|
|
231
|
+
echo '(^|/)(gnome-terminal(\.real)?([[:space:]]|$)|xterm([[:space:]]|$))'
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# Return 0 if a launched terminal is alive, 1 otherwise.
|
|
235
|
+
# ensureTerminal() uses this as its post-spawn liveness probe (the
|
|
236
|
+
# terminal-domain analogue of waitForPort — terminals have no port).
|
|
237
|
+
terminal_alive() {
|
|
238
|
+
pgrep -f "$(_terminal_pgrep_pattern)" >/dev/null 2>&1
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Return the first matching PID (used in logs). Empty string if none alive.
|
|
242
|
+
terminal_pid() {
|
|
243
|
+
pgrep -f "$(_terminal_pgrep_pattern)" 2>/dev/null | head -n 1
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# Kill all operator-launched terminal emulators. Pre-existing
|
|
247
|
+
# gnome-terminal-server is unaffected by the anchored regex.
|
|
248
|
+
kill_terminal_emulators() {
|
|
249
|
+
pkill -f "$(_terminal_pgrep_pattern)" 2>/dev/null || true
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
# Wait up to 1s for the spawned terminal to show up in pgrep.
|
|
253
|
+
# Mirrors wait_for_port's deadline semantics (3 × 0.3s = 0.9s wall-clock max).
|
|
254
|
+
wait_for_terminal() {
|
|
255
|
+
for _ in 1 2 3; do
|
|
256
|
+
if terminal_alive; then
|
|
257
|
+
return 0
|
|
258
|
+
fi
|
|
259
|
+
sleep 0.3
|
|
260
|
+
done
|
|
261
|
+
return 1
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
start_terminal_on() {
|
|
265
|
+
local target_display="$1"
|
|
266
|
+
local label="$2" # "vnc" | "native"
|
|
267
|
+
|
|
268
|
+
# Idempotency: if a terminal is already alive, do not spawn another.
|
|
269
|
+
# Matches ensureCdp's "CDP up → return true" branch. Display-switch is
|
|
270
|
+
# the caller's responsibility (ensureTerminal in vnc.ts kills first).
|
|
271
|
+
if terminal_alive; then
|
|
272
|
+
local existing_pid
|
|
273
|
+
existing_pid="$(terminal_pid)"
|
|
274
|
+
tlog "already-running pid=${existing_pid} display=${target_display}"
|
|
275
|
+
log "Terminal already running pid=${existing_pid} (${label})"
|
|
276
|
+
return 0
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
local resolved bin flags
|
|
280
|
+
if ! resolved="$(resolve_terminal_bin)"; then
|
|
281
|
+
return 1
|
|
282
|
+
fi
|
|
283
|
+
bin="${resolved%%$'\t'*}"
|
|
284
|
+
flags="${resolved#*$'\t'}"
|
|
285
|
+
|
|
286
|
+
log "Starting ${bin} ${flags} on ${target_display} (${label})"
|
|
287
|
+
|
|
288
|
+
# setsid -f detaches the process from our controlling terminal and this
|
|
289
|
+
# script's process group, so the spawned terminal survives vnc.sh exiting.
|
|
290
|
+
# Output redirected to the terminal-launch log (not /dev/null) so any
|
|
291
|
+
# spawn-time stderr is captured. Flags is unquoted on purpose so an empty
|
|
292
|
+
# value (xterm's case) does not produce a stray "" arg.
|
|
293
|
+
if [ -n "$flags" ]; then
|
|
294
|
+
DISPLAY="${target_display}" setsid -f "$bin" $flags >> "$TERMINAL_LOG" 2>&1 || true
|
|
295
|
+
else
|
|
296
|
+
DISPLAY="${target_display}" setsid -f "$bin" >> "$TERMINAL_LOG" 2>&1 || true
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
if wait_for_terminal; then
|
|
300
|
+
local pid
|
|
301
|
+
pid="$(terminal_pid)"
|
|
302
|
+
tlog "started pid=${pid} display=${target_display} cmd=\"${bin} ${flags}\" transport=${label}"
|
|
303
|
+
log "Terminal ready (${label}) pid=${pid}"
|
|
304
|
+
return 0
|
|
305
|
+
else
|
|
306
|
+
tlog "failed err=\"spawn detached but no terminal PID visible within 1s\" transport=${label} cmd=\"${bin} ${flags}\""
|
|
307
|
+
log "ERROR: terminal failed to appear in pgrep within 1s on ${target_display} (${label}) — investigate setsid -f detachment / --wait flag"
|
|
308
|
+
return 1
|
|
309
|
+
fi
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
start_terminal() {
|
|
313
|
+
start_terminal_on ":99" "vnc"
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
start_terminal_native() {
|
|
317
|
+
discover_native_session
|
|
318
|
+
start_terminal_on "${NATIVE_DISPLAY}" "native"
|
|
319
|
+
}
|
|
320
|
+
|
|
173
321
|
start_chrome_native() {
|
|
174
322
|
discover_native_session
|
|
175
323
|
|
|
@@ -279,6 +427,30 @@ case "${1:-}" in
|
|
|
279
427
|
start_chrome_native
|
|
280
428
|
;;
|
|
281
429
|
|
|
430
|
+
start-terminal)
|
|
431
|
+
start_terminal
|
|
432
|
+
;;
|
|
433
|
+
|
|
434
|
+
start-terminal-native)
|
|
435
|
+
start_terminal_native
|
|
436
|
+
;;
|
|
437
|
+
|
|
438
|
+
kill-terminal)
|
|
439
|
+
pid="$(terminal_pid)"
|
|
440
|
+
kill_terminal_emulators
|
|
441
|
+
if [ -n "$pid" ]; then
|
|
442
|
+
tlog "killed pid=${pid} reason=overlay-close"
|
|
443
|
+
else
|
|
444
|
+
tlog "killed-noop"
|
|
445
|
+
fi
|
|
446
|
+
;;
|
|
447
|
+
|
|
448
|
+
status-terminal)
|
|
449
|
+
# Exit 0 if an operator-launched terminal is running, 1 otherwise.
|
|
450
|
+
# Used by ensureTerminal() in vnc.ts as the in-process liveness probe.
|
|
451
|
+
terminal_alive && exit 0 || exit 1
|
|
452
|
+
;;
|
|
453
|
+
|
|
282
454
|
stop)
|
|
283
455
|
log "Stopping VNC stack"
|
|
284
456
|
kill_stale
|
|
@@ -290,7 +462,7 @@ case "${1:-}" in
|
|
|
290
462
|
;;
|
|
291
463
|
|
|
292
464
|
*)
|
|
293
|
-
echo "Usage: vnc.sh start | stop | start-chrome | start-chrome-native | status" >&2
|
|
465
|
+
echo "Usage: vnc.sh start | stop | start-chrome | start-chrome-native | start-terminal | start-terminal-native | kill-terminal | status-terminal | status" >&2
|
|
294
466
|
exit 1
|
|
295
467
|
;;
|
|
296
468
|
esac
|