Make private desktop tools opt-in and preserve host defaults
This commit is contained in:
1 parent
0ae1fccfba
commit
e84362b999
15 files changed
+219
-45
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# FrameBridge — command guide
|
||||
|
||||
Version: 0.3.2~compat.20261027.1 | Guide schema: 1
|
||||
Version: 0.3.2~compat.20261027.2 | Guide schema: 1
|
||||
|
||||
This reference is generated from the shared CLI/MCP guide. Use the matching installed version.
|
||||
|
||||
@@ -36,7 +36,7 @@ fb --json status
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
|
||||
~~~sh
|
||||
sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.1_amd64.deb
|
||||
sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.2_amd64.deb
|
||||
fb connect
|
||||
~~~
|
||||
|
||||
@@ -303,7 +303,7 @@ A CLI tty transcript describes a terminal and explicitly does not claim authenti
|
||||
|
||||
### Automatic graphical discovery
|
||||
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Xvfb and Openbox are optional package suggestions and are not installed for the normal connection. Only when you choose a private virtual desktop, install its tools explicitly with sudo apt install xvfb openbox xauth and rerun fb doctor. Installing Openbox can register it as the generic window-manager alternative; use your distribution settings to retain your preferred manager when needed. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.
|
||||
|
||||
~~~sh
|
||||
fb status
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topics": {
|
||||
"start": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "start",
|
||||
"title": "Start with FrameBridge",
|
||||
@@ -24,7 +24,7 @@
|
||||
"heading": "Install and connect",
|
||||
"body": "Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.",
|
||||
"commands": [
|
||||
"sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.1_amd64.deb",
|
||||
"sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.2_amd64.deb",
|
||||
"fb connect"
|
||||
]
|
||||
},
|
||||
@@ -64,12 +64,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "d6ef35f7879fa290843fbfcadf403270ec0e70e403b0230b3469659492385928"
|
||||
"guide_sha256": "4a85d9721ac3f758837a57126484b6cca56a598088f90365de2796f0b8d13766"
|
||||
},
|
||||
"commands": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "commands",
|
||||
"title": "Command reference",
|
||||
@@ -136,12 +136,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "a221dd430ceac8d1a7ddc859e8d38d9d07b07596000ff6a4e70826b0e7e4e127"
|
||||
"guide_sha256": "e8680e094c647dc991d7bc9e4800d3978502dad81cf101f10d072be88dc4e9a1"
|
||||
},
|
||||
"connection": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "connection",
|
||||
"title": "Connect and disconnect ChatGPT",
|
||||
@@ -190,12 +190,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "91b8d6d7e531ba84512de58d561b71c92212160ddabc78349c459299cd96751a"
|
||||
"guide_sha256": "0c8c16baf4d28e9b37154dad049193aeae252813b18eba3159cf437918eb3416"
|
||||
},
|
||||
"lifecycle": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "lifecycle",
|
||||
"title": "Services, connection intent and boot policy",
|
||||
@@ -256,12 +256,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "df3515c6c874353edcce43e4869021409ad0b7f28f194dd93296911228746cdd"
|
||||
"guide_sha256": "0e3d6c96084e40b07d7373f3f7dbe22aa7659e59ec7cff57383026918dd0948c"
|
||||
},
|
||||
"nodes": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "nodes",
|
||||
"title": "Central and node operation",
|
||||
@@ -310,12 +310,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "3f98ed2af8adb3b0cd50967fe98fac90d26c3c2034fc7945593d846b4b6c3910"
|
||||
"guide_sha256": "a0a317d230abb52bd673bb82453c0c17d73d31e30b1766e4247ef80ada02aa2a"
|
||||
},
|
||||
"terminals": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "terminals",
|
||||
"title": "Persistent Bash terminals",
|
||||
@@ -376,12 +376,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "b2fdf20610cf7fdfeecaa822ae5ac4297e55c9a0ba7d5e86c06ba50aca9588a2"
|
||||
"guide_sha256": "e9ab7535894f07a7aa309d0f4f02d36d37c65143006f8bc67b9f12a1e00f0f5a"
|
||||
},
|
||||
"screens": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "screens",
|
||||
"title": "Authentic screen observation and input",
|
||||
@@ -393,7 +393,7 @@
|
||||
},
|
||||
{
|
||||
"heading": "Automatic graphical discovery",
|
||||
"body": "The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.",
|
||||
"body": "The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Xvfb and Openbox are optional package suggestions and are not installed for the normal connection. Only when you choose a private virtual desktop, install its tools explicitly with sudo apt install xvfb openbox xauth and rerun fb doctor. Installing Openbox can register it as the generic window-manager alternative; use your distribution settings to retain your preferred manager when needed. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.",
|
||||
"commands": [
|
||||
"fb status",
|
||||
"fb doctor"
|
||||
@@ -426,12 +426,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "1ce747282da1d9d1504790607888af131666758dd28b4a47799ed4c59427436b"
|
||||
"guide_sha256": "3d37bc0fac316fa50275f1a9f38f15e87aef435593c694aebaacf4666734e1fd"
|
||||
},
|
||||
"diagnostics": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "diagnostics",
|
||||
"title": "Status and diagnosis",
|
||||
@@ -476,12 +476,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "1e85490e5b9925afb588bf536b155cf3cbfaef3997d21be94856fd1997372497"
|
||||
"guide_sha256": "9cdab418287a3fcf283258782d117813bb45d23183101e5b7ee8eeb162cd38b4"
|
||||
},
|
||||
"security": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "security",
|
||||
"title": "Authorization, distribution and safe operation",
|
||||
@@ -516,12 +516,12 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "2982ca7b623a972097b5e04c309b682fefd91754d0c4c50fc802420c14c9c68d"
|
||||
"guide_sha256": "64ecf0b4e25dec7faffae90ef3d48ce732f5ec2c7fb815815a625ae124230c9e"
|
||||
},
|
||||
"compatibility": {
|
||||
"ok": true,
|
||||
"product": "FrameBridge",
|
||||
"version": "0.3.2~compat.20261027.1",
|
||||
"version": "0.3.2~compat.20261027.2",
|
||||
"guide_schema": 1,
|
||||
"topic": "compatibility",
|
||||
"title": "Compatibility with earlier commands",
|
||||
@@ -564,8 +564,8 @@
|
||||
],
|
||||
"read_only": true,
|
||||
"capabilities_verified": false,
|
||||
"guide_sha256": "54b70093617e6992de04d41ddd367ffdd9a6bd6c317c99a886d6391277c55f6b"
|
||||
"guide_sha256": "f622466edc45196d42390129d8a32424e1b0e64b7751688790061b1e8b5941d2"
|
||||
}
|
||||
},
|
||||
"guide_sha256": "ab3cce8d01d63a34fa2634738a9e0b37c89f5e1f51ba88d2b33abf9a872ad7ab"
|
||||
"guide_sha256": "f83c632796c7cdb3489683b1c4584f0fedec11f4aeb9321430f1a709888d6965"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
# FrameBridge — command guide
|
||||
|
||||
Version: 0.3.2~compat.20261027.1 | Guide schema: 1
|
||||
Version: 0.3.2~compat.20261027.2 | Guide schema: 1
|
||||
|
||||
This reference is generated from the shared CLI/MCP guide. Use the matching installed version.
|
||||
|
||||
@@ -36,7 +36,7 @@ fb --json status
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
|
||||
~~~sh
|
||||
sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.1_amd64.deb
|
||||
sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.2_amd64.deb
|
||||
fb connect
|
||||
~~~
|
||||
|
||||
@@ -303,7 +303,7 @@ A CLI tty transcript describes a terminal and explicitly does not claim authenti
|
||||
|
||||
### Automatic graphical discovery
|
||||
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Xvfb and Openbox are optional package suggestions and are not installed for the normal connection. Only when you choose a private virtual desktop, install its tools explicitly with sudo apt install xvfb openbox xauth and rerun fb doctor. Installing Openbox can register it as the generic window-manager alternative; use your distribution settings to retain your preferred manager when needed. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.
|
||||
|
||||
~~~sh
|
||||
fb status
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# FrameBridge — Debian, Ubuntu and Linux Mint installation and first use
|
||||
|
||||
Version: 0.3.2~compat.20261027.1 | Guide schema: 1
|
||||
Version: 0.3.2~compat.20261027.2 | Guide schema: 1
|
||||
|
||||
This reference is generated from the shared CLI/MCP guide. Use the matching installed version.
|
||||
|
||||
@@ -36,7 +36,7 @@ fb --json status
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
|
||||
~~~sh
|
||||
sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.1_amd64.deb
|
||||
sudo apt install ./framebridge-expanded_0.3.2~compat.20261027.2_amd64.deb
|
||||
fb connect
|
||||
~~~
|
||||
|
||||
@@ -303,7 +303,7 @@ A CLI tty transcript describes a terminal and explicitly does not claim authenti
|
||||
|
||||
### Automatic graphical discovery
|
||||
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Xvfb and Openbox are optional package suggestions and are not installed for the normal connection. Only when you choose a private virtual desktop, install its tools explicitly with sudo apt install xvfb openbox xauth and rerun fb doctor. Installing Openbox can register it as the generic window-manager alternative; use your distribution settings to retain your preferred manager when needed. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.
|
||||
|
||||
~~~sh
|
||||
fb status
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH FRAMEBRIDGE 1 "2026-10-27" "FrameBridge 0.3.2~compat.20261027.1" "User Commands"
|
||||
.TH FRAMEBRIDGE 1 "2026-10-27" "FrameBridge 0.3.2~compat.20261027.2" "User Commands"
|
||||
.SH NAME
|
||||
framebridge, fb \- use local Linux tools and enrolled nodes with ChatGPT
|
||||
.SH SYNOPSIS
|
||||
@@ -21,7 +21,7 @@ fb \-\-json status
|
||||
Install the .deb built for your exact Debian 13, Ubuntu 24.04/26.04, Linux Mint 22.x or LMDE 7 amd64 profile. These compatibility candidates still require installation and lifecycle validation on each target. Select the artifact from the matching profile folder and keep the distribution system Python. Normal first connection does not require a separate setup, doctor or start sequence. Run connect in a normal interactive terminal; advanced setup is optional for central/node roles and public settings.
|
||||
.PP
|
||||
.nf
|
||||
sudo apt install ./framebridge\-expanded_0.3.2~compat.20261027.1_amd64.deb
|
||||
sudo apt install ./framebridge\-expanded_0.3.2~compat.20261027.2_amd64.deb
|
||||
fb connect
|
||||
.fi
|
||||
.SS Updating and removal
|
||||
@@ -210,7 +210,7 @@ fb status console
|
||||
.SS Text transcripts and pixels
|
||||
A CLI tty transcript describes a terminal and explicitly does not claim authentic pixels. MCP real\-screen CUA\-TTY and graphical screen tools operate through the real screen client and capture\-linked interaction. Use those supported observation tools when authentic complete screen evidence is required. Never present a reconstructed terminal, animation or partial invented view as a real screenshot.
|
||||
.SS Automatic graphical discovery
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA\-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual\-desktop settings remain advanced options and are labelled separately from physical\-screen evidence. Physical\-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full\-desktop capture or input.
|
||||
The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA\-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual\-desktop settings remain advanced options and are labelled separately from physical\-screen evidence. Xvfb and Openbox are optional package suggestions and are not installed for the normal connection. Only when you choose a private virtual desktop, install its tools explicitly with sudo apt install xvfb openbox xauth and rerun fb doctor. Installing Openbox can register it as the generic window\-manager alternative; use your distribution settings to retain your preferred manager when needed. Physical\-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full\-desktop capture or input.
|
||||
.PP
|
||||
.nf
|
||||
fb status
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "0.3.2~compat.20261027.1"
|
||||
__version__ = "0.3.2~compat.20261027.2"
|
||||
@@ -441,6 +441,12 @@ def _doctor_command(args, state):
|
||||
print("Missing command: " + str(item))
|
||||
for item in value.get("issues", []):
|
||||
print("Issue: " + str(item))
|
||||
virtual_missing = value.get("optional_missing_commands", {}).get("private_desktop", [])
|
||||
if virtual_missing:
|
||||
print("Optional private desktop tools missing: " + ", ".join(virtual_missing))
|
||||
hint = value.get("optional_install_commands", {}).get("private_desktop")
|
||||
if hint:
|
||||
print("Install only for an explicit virtual desktop: " + hint)
|
||||
print("This checks prerequisites; use fb status to inspect the current connection.")
|
||||
return _result_code(value)
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@ from .real_screen.screen_capture import CaptureError, foreground, _x11_context
|
||||
BINDING = "FRAMEBRIDGE_DISPLAY_BINDING"
|
||||
GRAPHICAL_COMMANDS = ("wmctrl", "xdotool", "xwininfo", "xclip")
|
||||
VIRTUAL_COMMANDS = ("Xvfb", "openbox", "xauth")
|
||||
VIRTUAL_INSTALL_COMMAND = "sudo apt install xvfb openbox xauth"
|
||||
|
||||
|
||||
def missing_virtual_commands(env):
|
||||
return [name for name in VIRTUAL_COMMANDS if not shutil.which(name, path=env.get("PATH"))]
|
||||
|
||||
|
||||
def missing_graphical_commands(env):
|
||||
|
||||
@@ -302,7 +302,7 @@ GUIDE = {'start': {'title': 'Start with FrameBridge',
|
||||
'partial invented view as a real screenshot.',
|
||||
'commands': []},
|
||||
{'heading': 'Automatic graphical discovery',
|
||||
'body': 'The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.',
|
||||
'body': 'The installer and control console stay text interfaces in both TTY and a desktop terminal. Compatible graphical CUA is discovered automatically; availability depends on the actual display/session and permissions. A loaded module is not proof that real pixel capture or input succeeded. New profiles use adaptive auto mode: without a verified active X11 session the runtime provides FBEXEC, FBTERM and CUA-TTY in text mode, without requiring a graphical login or automatically creating a private desktop. Explicit virtual-desktop settings remain advanced options and are labelled separately from physical-screen evidence. Xvfb and Openbox are optional package suggestions and are not installed for the normal connection. Only when you choose a private virtual desktop, install its tools explicitly with sudo apt install xvfb openbox xauth and rerun fb doctor. Installing Openbox can register it as the generic window-manager alternative; use your distribution settings to retain your preferred manager when needed. Physical-screen capture and input currently require the supported active X11 session. Native Wayland control is not validated; showing an XWayland window does not establish full-desktop capture or input.',
|
||||
'commands': ['fb status', 'fb doctor']},
|
||||
{'heading': 'Frame-buffer read access',
|
||||
'body': 'screen-access is a narrow, explicit administrative permission '
|
||||
|
||||
@@ -151,9 +151,9 @@ def doctor(state):
|
||||
commands = ["bash", "tmux", "tee", "flock", "awk", "sha256sum", "timeout",
|
||||
"dbus-run-session", "ip", "lsblk", "systemctl", "systemd-run", "sudo", "uptime"]
|
||||
missing = [x for x in commands if not shutil.which(x, path=env.get("PATH"))]
|
||||
from .display_context import missing_graphical_commands, VIRTUAL_COMMANDS
|
||||
from .display_context import missing_graphical_commands, missing_virtual_commands, VIRTUAL_INSTALL_COMMAND
|
||||
graphical_missing = missing_graphical_commands(env)
|
||||
virtual_missing = [x for x in VIRTUAL_COMMANDS if not shutil.which(x, path=env.get("PATH"))]
|
||||
virtual_missing = missing_virtual_commands(env)
|
||||
console_missing = [x for x in ("gnome-terminal", "xterm") if not shutil.which(x, path=env.get("PATH"))]
|
||||
if config.get("headless") in ("no", False):
|
||||
missing.extend(graphical_missing)
|
||||
@@ -182,8 +182,10 @@ def doctor(state):
|
||||
"display_context": display_context,
|
||||
"capabilities": {"fbexec": not missing, "fbterm": not missing, "cua_tty_module": True,
|
||||
"graphical_cua": display_context.get("kind") in ("physical-x11", "virtual-x11") and not graphical_missing,
|
||||
"private_desktop": compatible and not missing and not graphical_missing and not virtual_missing,
|
||||
"mode": "graphical" if display_context.get("kind") in ("physical-x11", "virtual-x11") else "text"},
|
||||
"optional_missing_commands": {"graphical_cua": graphical_missing, "private_desktop": virtual_missing, "graphical_terminal": console_missing},
|
||||
"optional_install_commands": {"private_desktop": VIRTUAL_INSTALL_COMMAND if virtual_missing else None},
|
||||
"network": inspect_network(),
|
||||
"role": config["role"], "issues": issues}
|
||||
|
||||
@@ -372,6 +374,10 @@ def _manager(state):
|
||||
from .display_context import bind_text
|
||||
bind_text(env, state, reason="PHYSICAL_X11_UNAVAILABLE_TEXT_TOOLS_ACTIVE")
|
||||
if private_display:
|
||||
from .display_context import missing_virtual_commands, VIRTUAL_INSTALL_COMMAND
|
||||
missing_virtual = missing_virtual_commands(env)
|
||||
if missing_virtual:
|
||||
raise RuntimeError("Private virtual desktop tools are missing: " + ", ".join(missing_virtual) + ". Install them explicitly: " + VIRTUAL_INSTALL_COMMAND)
|
||||
display = next((n for n in range(140, 240) if not Path("/tmp/.X11-unix/X" + str(n)).exists() and not Path("/tmp/.X" + str(n) + "-lock").exists()), None)
|
||||
if display is None:
|
||||
raise RuntimeError("No private X11 display number is available.")
|
||||
@@ -424,7 +430,11 @@ def start(state):
|
||||
return current
|
||||
checks = doctor(state)
|
||||
if not checks["ok"]:
|
||||
return {"ok": False, "error": "Preflight failed. Run framebridge doctor.", "doctor": checks}
|
||||
error = "Preflight failed. Run framebridge doctor."
|
||||
hint = checks.get("optional_install_commands", {}).get("private_desktop")
|
||||
if config.get("headless") in ("yes", True) and hint:
|
||||
error = "Private virtual desktop tools are missing. Install them explicitly: " + hint + ". Then run framebridge doctor."
|
||||
return {"ok": False, "error": error, "doctor": checks}
|
||||
for name in ("logs", "run"):
|
||||
private_dir(state / name)
|
||||
for name in ("ready.json", "last-error.json", "worker.json"):
|
||||
|
||||
@@ -169,6 +169,14 @@ def maintainer_prerm(version):
|
||||
)
|
||||
|
||||
|
||||
def package_relationships(profile):
|
||||
"""Keep the opt-in virtual window manager out of automatic APT installs."""
|
||||
return {
|
||||
"Depends": "python3 (>= " + profile["python_minor"] + "), python3 (<< 3." + str(int(profile["python_minor"].split(".")[1]) + 1) + "), libc6 (>= 2.34), libgcc-s1, python3-cryptography, sudo, libpam-systemd, dbus-user-session, udev, libx11-6, ca-certificates, iproute2, procps, systemd, bash, tmux, coreutils, util-linux, mawk | gawk, gnome-terminal, wmctrl, xdotool, x11-utils, xclip, imagemagick, xterm, dbus-x11, xauth, x11-xkb-utils, fonts-dejavu-core",
|
||||
"Suggests": "xvfb, openbox",
|
||||
}
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
global VERSION, PUBLIC_FILENAME, RELEASE, STAGE, PREFIX
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
@@ -253,6 +261,15 @@ def main(argv=None):
|
||||
for file in sorted((BASE / "docs").glob("*")):
|
||||
if file.is_file():
|
||||
shutil.copy2(file, docs / file.name)
|
||||
# Ship the same owner/agent entry points as the reviewed source repository.
|
||||
user_guides = {"README.md": ROOT / "README.md",
|
||||
"FRAMEBRIDGE-AGENT-START.md": ROOT / "docs/FRAMEBRIDGE-AGENT-START.md"}
|
||||
for name, source in user_guides.items():
|
||||
if not source.is_file() or source.is_symlink():
|
||||
raise RuntimeError("Required user guide is missing or not a regular file: " + str(source))
|
||||
for destination in (PREFIX / "docs", docs):
|
||||
destination.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(source, destination / name)
|
||||
readme = (BASE / "docs/DEBIAN-TUTORIAL.md").read_bytes()
|
||||
(docs / "README.Debian.gz").write_bytes(gzip.compress(readme, mtime=EPOCH))
|
||||
install_legal_material(docs)
|
||||
@@ -264,12 +281,14 @@ def main(argv=None):
|
||||
control = STAGE / "DEBIAN"
|
||||
control.mkdir()
|
||||
installed = sum(p.stat().st_size for p in STAGE.rglob("*") if p.is_file() and not p.is_symlink())
|
||||
relationships = package_relationships(profile)
|
||||
(control / "control").write_text(
|
||||
"Package: " + PACKAGE + "\nVersion: " + VERSION + "\nSection: utils\nPriority: optional\nArchitecture: amd64\n"
|
||||
"Maintainer: FrameBridge <framebridge@localhost>\n"
|
||||
"Homepage: https://framebridge.betancourt.one/\n"
|
||||
"Installed-Size: " + str((installed + 1023) // 1024) + "\n"
|
||||
"Depends: python3 (>= " + profile["python_minor"] + "), python3 (<< 3." + str(int(profile["python_minor"].split(".")[1]) + 1) + "), libc6 (>= 2.34), libgcc-s1, python3-cryptography, sudo, libpam-systemd, dbus-user-session, udev, libx11-6, ca-certificates, iproute2, procps, systemd, bash, tmux, coreutils, util-linux, mawk | gawk, gnome-terminal, wmctrl, xdotool, x11-utils, xclip, imagemagick, xterm, xvfb, openbox, dbus-x11, xauth, x11-xkb-utils, fonts-dejavu-core\n"
|
||||
"Depends: " + relationships["Depends"] + "\n"
|
||||
"Suggests: " + relationships["Suggests"] + "\n"
|
||||
"Description: FrameBridge with standalone, central and node modes\n"
|
||||
" Original execution and desktop programs, a real persistent terminal,\n"
|
||||
" authenticated node enrollment, and a manual ChatGPT tunnel wizard.\n"
|
||||
|
||||
@@ -11,7 +11,7 @@ from pathlib import Path
|
||||
import re
|
||||
|
||||
|
||||
CANDIDATE_VERSION = "0.3.2~compat.20261027.1"
|
||||
CANDIDATE_VERSION = "0.3.2~compat.20261027.2"
|
||||
VENDOR_RELATIVE = Path("original/geranas/FrameBridge-Fleet-Hub/vendor")
|
||||
SOURCE_VENDOR = Path("sources/original-geranas/FrameBridge-Fleet-Hub/vendor")
|
||||
GLIBC_BASELINE = (2, 34)
|
||||
|
||||
@@ -86,7 +86,10 @@ class HostCredentialAutostartTests(unittest.TestCase):
|
||||
def test_node_255_startup_does_not_need_a_credential_backend(self):
|
||||
config.save_config(self.state, config.default_config(role="node"))
|
||||
self.helper.side_effect = RuntimeError("missing helper")
|
||||
autostart._enable(self.state, confirm=False, start_now=False)
|
||||
# The absent-helper case must not depend on another package being
|
||||
# installed on the machine running this source-checkout test.
|
||||
with mock.patch.object(autostart, "_helper_present", return_value=False):
|
||||
autostart._enable(self.state, confirm=False, start_now=False)
|
||||
self.helper.assert_not_called(); self.prompt.assert_not_called()
|
||||
self.assertFalse(self.operations("protect"))
|
||||
self.assertFalse(any(args[:2] == ["systemd-creds", "--version"] for args, _ in self.calls))
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
"""Optional private desktops never change normal installation/readiness."""
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest.mock import patch, Mock
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'packaging'))
|
||||
from build_expanded import package_relationships
|
||||
from build_profiles import PROFILES, target_profile
|
||||
from framebridge_expanded import cli, config, display_context as dc, runtime
|
||||
|
||||
|
||||
class DesktopPackageRelationships(unittest.TestCase):
|
||||
def test_no_automatic_virtual_desktop_in_any_target(self):
|
||||
for target in PROFILES:
|
||||
with self.subTest(target=target):
|
||||
fields = package_relationships(target_profile(target))
|
||||
def packages(value):
|
||||
return {re.match(r'[a-z0-9+.-]+', item.strip()).group()
|
||||
for item in re.split(r'[,|]', value) if item.strip()}
|
||||
required = packages(fields['Depends'])
|
||||
recommended = packages(fields.get('Recommends', ''))
|
||||
self.assertTrue({'openbox', 'xvfb'}.isdisjoint(required | recommended))
|
||||
self.assertEqual({'openbox', 'xvfb'}, set(fields['Suggests'].split(', ')))
|
||||
self.assertIn('xauth', required)
|
||||
self.assertIn('tmux', required)
|
||||
|
||||
|
||||
class OptionalPrivateDesktop(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.temporary = tempfile.TemporaryDirectory(prefix='fb-optional-desktop-')
|
||||
self.addCleanup(self.temporary.cleanup)
|
||||
self.state = Path(self.temporary.name)
|
||||
config.save_config(self.state, config.default_config())
|
||||
self.env = {'PATH': '/fixture/bin'}
|
||||
dc.bind_text(self.env, self.state)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def prerequisites(self, missing, physical=False):
|
||||
def which(name, path=None):
|
||||
return None if name in missing else '/fixture/bin/' + name
|
||||
compatible = {'ok': True, 'profile_id': 'debian13-amd64-py313',
|
||||
'issues': [], 'warnings': []}
|
||||
context = {'kind': 'physical-x11', 'physical': True} if physical else {'kind': 'text', 'physical': False}
|
||||
with patch.object(runtime, 'environment', return_value=dict(self.env)), \
|
||||
patch.object(runtime.shutil, 'which', side_effect=which), \
|
||||
patch.object(runtime, '_display_ok', return_value=physical), \
|
||||
patch.object(dc, 'describe', return_value=context), \
|
||||
patch('framebridge_expanded.network.inspect', return_value={}), \
|
||||
patch('framebridge_expanded.compatibility.assess', return_value=compatible):
|
||||
yield
|
||||
|
||||
def mode(self, value):
|
||||
settings = config.load_config(self.state)
|
||||
settings['headless'] = value
|
||||
config.save_config(self.state, settings)
|
||||
|
||||
def test_normal_text_mode_ready_without_optional_tools(self):
|
||||
with self.prerequisites({'openbox', 'Xvfb'}):
|
||||
result = runtime.doctor(self.state)
|
||||
self.assertTrue(result['ok'])
|
||||
self.assertEqual([], result['missing_commands'])
|
||||
self.assertTrue(result['capabilities']['fbexec'])
|
||||
self.assertTrue(result['capabilities']['fbterm'])
|
||||
self.assertFalse(result['capabilities']['private_desktop'])
|
||||
self.assertEqual(['Xvfb', 'openbox'], result['optional_missing_commands']['private_desktop'])
|
||||
self.assertEqual(dc.VIRTUAL_INSTALL_COMMAND, result['optional_install_commands']['private_desktop'])
|
||||
|
||||
def test_existing_physical_x11_does_not_require_virtual_tools(self):
|
||||
self.mode('no')
|
||||
with self.prerequisites({'openbox', 'Xvfb'}, physical=True):
|
||||
result = runtime.doctor(self.state)
|
||||
self.assertTrue(result['ok'])
|
||||
self.assertTrue(result['capabilities']['graphical_cua'])
|
||||
self.assertFalse(result['capabilities']['private_desktop'])
|
||||
self.assertEqual([], result['missing_commands'])
|
||||
|
||||
def test_explicit_virtual_start_fails_before_spawning_with_install_hint(self):
|
||||
self.mode('yes')
|
||||
with self.prerequisites({'openbox'}), \
|
||||
patch.object(runtime, 'status', return_value={'running': False}), \
|
||||
patch.object(runtime, '_spawn') as spawn:
|
||||
result = runtime.start(self.state)
|
||||
self.assertFalse(result['ok'])
|
||||
self.assertIn('openbox', result['doctor']['missing_commands'])
|
||||
self.assertIn(dc.VIRTUAL_INSTALL_COMMAND, result['error'])
|
||||
spawn.assert_not_called()
|
||||
|
||||
def test_manager_rechecks_missing_tool_before_creating_any_child(self):
|
||||
self.mode('yes')
|
||||
with self.prerequisites({'openbox'}), \
|
||||
patch.object(runtime.signal, 'signal'), \
|
||||
patch.object(runtime, '_spawn') as spawn:
|
||||
with self.assertRaisesRegex(RuntimeError, 'Private virtual desktop tools are missing: openbox'):
|
||||
runtime._manager(self.state)
|
||||
spawn.assert_not_called()
|
||||
error = json.loads((self.state/'last-error.json').read_text())['error']
|
||||
self.assertIn(dc.VIRTUAL_INSTALL_COMMAND, error)
|
||||
|
||||
def test_virtual_tools_available_with_no_optional_install_hint(self):
|
||||
with self.prerequisites(set()):
|
||||
result = runtime.doctor(self.state)
|
||||
self.assertTrue(result['capabilities']['private_desktop'])
|
||||
self.assertIsNone(result['optional_install_commands']['private_desktop'])
|
||||
|
||||
def test_human_doctor_explains_optional_install_without_failing(self):
|
||||
fake_runtime = SimpleNamespace(doctor=Mock(return_value={
|
||||
'ok': True, 'display': 'text tools', 'missing_commands': [], 'issues': [],
|
||||
'platform_compatibility': {'ok': True},
|
||||
'optional_missing_commands': {'private_desktop': ['openbox']},
|
||||
'optional_install_commands': {'private_desktop': dc.VIRTUAL_INSTALL_COMMAND}}))
|
||||
stdout = io.StringIO()
|
||||
with patch.object(cli, '_module', return_value=fake_runtime), \
|
||||
contextlib.redirect_stdout(stdout):
|
||||
code = cli.main(['--state-dir', str(self.state), 'doctor'])
|
||||
self.assertEqual(0, code)
|
||||
self.assertIn('Optional private desktop tools missing: openbox', stdout.getvalue())
|
||||
self.assertIn(dc.VIRTUAL_INSTALL_COMMAND, stdout.getvalue())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -81,7 +81,10 @@ class UninstallTests(unittest.TestCase):
|
||||
def test_all_users_drops_privileges_and_includes_service_accounts(self):
|
||||
accounts = [SimpleNamespace(pw_name="testservice", pw_uid=104, pw_dir="/var/lib/testservice"), SimpleNamespace(pw_name="testperson", pw_uid=1000, pw_dir="/home/testperson"), SimpleNamespace(pw_name="nobody", pw_uid=65534, pw_dir="/nonexistent")]
|
||||
run = mock.Mock(return_value=SimpleNamespace(returncode=0))
|
||||
with mock.patch.object(uninstall.os, "getuid", return_value=0):uninstall.cleanup_all_users(run=run, accounts=accounts)
|
||||
with mock.patch.object(uninstall.os, "getuid", return_value=0), \
|
||||
mock.patch.object(uninstall, "purge_credential_helpers") as purge:
|
||||
uninstall.cleanup_all_users(run=run, accounts=accounts)
|
||||
purge.assert_called_once_with(run=run)
|
||||
self.assertEqual(run.call_count, 2)
|
||||
for call, name in zip(run.call_args_list, ["testservice", "testperson"]):
|
||||
args = call.args[0]
|
||||
|
||||
Reference in new issue
Block a user