feat(release): stage GitWorkshop and next node updates

This commit is contained in:
archipelago
2026-09-09 18:15:21 -04:00
parent 973356df16
commit f5c0ba85cd
97 changed files with 5716 additions and 1327 deletions
+38 -3
View File
@@ -18,7 +18,7 @@ from typing import Any
import yaml
SYNC_FIELDS = ("title", "version", "description", "dockerImage", "category", "tier", "icon", "repoUrl")
SYNC_FIELDS = ("title", "version", "description", "dockerImage", "category", "tier", "icon", "repoUrl", "maintainerNpub")
def load_manifests(apps_dir: Path) -> dict[str, dict[str, Any]]:
@@ -43,15 +43,20 @@ def metadata(app: dict[str, Any]) -> dict[str, Any]:
def manifest_catalog_values(app: dict[str, Any]) -> dict[str, str]:
meta = metadata(app)
container = app.get("container") if isinstance(app.get("container"), dict) else {}
build = container.get("build") if isinstance(container.get("build"), dict) else {}
values = {
"title": app.get("name"),
"version": app.get("version"),
"description": app.get("description"),
"dockerImage": container.get("image"),
# The install RPC still requires an image-shaped identifier before it
# dispatches to the manifest orchestrator. For build-source apps this
# is the local tag the orchestrator creates, not a registry image.
"dockerImage": container.get("image") or build.get("tag"),
"category": app.get("category") or meta.get("category"),
"tier": meta.get("tier"),
"icon": meta.get("icon"),
"repoUrl": meta.get("repo") or meta.get("repoUrl") or meta.get("source"),
"maintainerNpub": meta.get("maintainer_npub"),
}
return {key: str(value) for key, value in values.items() if value is not None and str(value).strip()}
@@ -98,6 +103,19 @@ def manifest_opens_in_new_tab(app: dict[str, Any]) -> bool:
return launch.get("open_in_new_tab") is True
def manifest_requires_host_frame(app: dict[str, Any]) -> bool:
"""Return whether an app must remain inside the dashboard document.
The Android companion normally promotes app sessions to its native browser
overlay. Apps that consume a parent-frame integration (for example the
NIP-07 signer bridge) must stay embedded instead.
"""
launch = metadata(app).get("launch")
if not isinstance(launch, dict):
return False
return launch.get("requires_host_frame") is True
def ts_string(value: str) -> str:
return json.dumps(value, ensure_ascii=True)
@@ -106,6 +124,7 @@ def render_app_session_config(manifests: dict[str, dict[str, Any]]) -> str:
ports: dict[str, int] = {}
titles: dict[str, str] = {}
new_tab_apps: list[str] = []
host_frame_apps: list[str] = []
for app_id, app in sorted(manifests.items()):
name = app.get("name")
if isinstance(name, str) and name.strip():
@@ -113,8 +132,17 @@ def render_app_session_config(manifests: dict[str, dict[str, Any]]) -> str:
port = manifest_launch_port(app)
if port:
ports[app_id] = port
if manifest_opens_in_new_tab(app):
opens_in_new_tab = manifest_opens_in_new_tab(app)
requires_host_frame = manifest_requires_host_frame(app)
if opens_in_new_tab and requires_host_frame:
raise ValueError(
f"{app_id}: metadata.launch.open_in_new_tab and "
"requires_host_frame cannot both be true"
)
if opens_in_new_tab:
new_tab_apps.append(app_id)
if requires_host_frame:
host_frame_apps.append(app_id)
lines = [
"/** Generated by scripts/generate-app-catalog.py. Do not edit manually. */",
@@ -137,6 +165,13 @@ def render_app_session_config(manifests: dict[str, dict[str, Any]]) -> str:
])
for app_id in new_tab_apps:
lines.append(f" {ts_string(app_id)},")
lines.extend([
"])",
"",
"export const GENERATED_HOST_FRAME_APPS = new Set<string>([",
])
for app_id in host_frame_apps:
lines.append(f" {ts_string(app_id)},")
lines.extend(["])", ""])
return "\n".join(lines)