Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions apps/staged/justfile
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,119 @@ dev repo="":
{{ if repo != "" { "export STAGED_REPO=" + repo } else { "" } }}
pnpm exec tauri dev --config "$TAURI_CONFIG"

# Run with the HTTPS web server enabled for phone/browser access.
# Requires PEM cert/key files and a hostname covered by the certificate.
dev-web repo="":
#!/usr/bin/env bash
set -euo pipefail

[[ -d node_modules ]] || pnpm install

if [[ -z "${STAGED_WEB_CERT_PATH:-}" || -z "${STAGED_WEB_KEY_PATH:-}" || -z "${STAGED_WEB_HOST:-}" ]]; then
printf '%s\n' \
'Error: `just dev-web` serves browser access over HTTPS.' \
'Provide PEM certificate/key files and a hostname covered by the certificate:' \
'' \
' STAGED_WEB_CERT_PATH=/path/to/cert.pem \' \
' STAGED_WEB_KEY_PATH=/path/to/key.pem \' \
' STAGED_WEB_HOST=hostname.example.com \' \
' just dev-web' >&2
exit 1
fi

VITE_PORT=$(python3 -c "import hashlib,os; h=int(hashlib.sha256(os.getcwd().encode()).hexdigest(),16); print(10000 + h % 55000)")
export VITE_PORT
export STAGED_WEB_SERVER=1
TAURI_CONFIG="{\"build\":{\"devUrl\":\"https://${STAGED_WEB_HOST}:${VITE_PORT}\",\"beforeDevCommand\":\"exec ./node_modules/.bin/vite --port ${VITE_PORT} --strictPort --host 0.0.0.0\"}}"

echo "Starting on https://${STAGED_WEB_HOST}:${VITE_PORT} (HTTPS web server on :5175)"
{{ if repo != "" { "export STAGED_REPO=" + repo } else { "" } }}
pnpm exec tauri dev --config "$TAURI_CONFIG"

# Build and run the release app with the HTTPS web server. Requires PEM cert/key files.
run-web:
#!/usr/bin/env bash
set -euo pipefail

# Unlike `dev-web` there is no Vite/HMR: the desktop window and the phone
# get the same built bundle, so this is the path that exercises the service
# worker and the production frontend. One Staged process at a time — a
# `just dev`/`dev-web` instance elsewhere shares ~/.staged and the port.

[[ -d node_modules ]] || pnpm install

if [[ -z "${STAGED_WEB_CERT_PATH:-}" || -z "${STAGED_WEB_KEY_PATH:-}" ]]; then
printf '%s\n' \
'Error: `just run-web` serves browser access over HTTPS.' \
'Provide PEM certificate/key files covering the hostname you browse to:' \
'' \
' STAGED_WEB_CERT_PATH=/path/to/cert.pem \' \
' STAGED_WEB_KEY_PATH=/path/to/key.pem \' \
' just run-web' >&2
exit 1
fi

# `tauri build` runs `pnpm run build` first, so dist/ is always regenerated
# here. The window's assets are compiled into the binary while the server
# reads dist/ from disk — rebuilding both together keeps them in sync.
# `--bundles app` skips the DMG that `just build` would also produce.
pnpm exec tauri build --bundles app

# Cargo resolves a relative CARGO_TARGET_DIR against its own cwd (src-tauri)
TARGET_DIR="$(pwd)/src-tauri/target"
if [[ -n "${CARGO_TARGET_DIR:-}" ]]; then
case "$CARGO_TARGET_DIR" in
/*) TARGET_DIR="$CARGO_TARGET_DIR" ;;
*) TARGET_DIR="$(pwd)/src-tauri/$CARGO_TARGET_DIR" ;;
esac
fi

APP_BIN="$TARGET_DIR/release/bundle/macos/Staged.app/Contents/MacOS/Staged"
if [[ ! -x "$APP_BIN" ]]; then
echo "Error: no app bundle at $APP_BIN" >&2
exit 1
fi

export STAGED_WEB_SERVER=1

# The server locates the frontend by trying <exe>/../dist, then
# <exe>/../../../../dist, then ../dist relative to the cwd. Only the last
# one resolves for a bundled release binary, so launch from src-tauri to
# make ../dist mean apps/staged/dist. Anywhere else serves 404s.
cd src-tauri

"$APP_BIN" &
APP_PID=$!
trap 'kill "$APP_PID" 2>/dev/null || true' EXIT

# Release builds install no logger, so web server failures (bad cert path,
# :5175 already bound by another Staged instance) are completely silent.
# Probe the server rather than trusting that the app launched.
STATUS=000
for _ in $(seq 1 30); do
kill -0 "$APP_PID" 2>/dev/null || break
STATUS=$(curl -sk --max-time 2 -o /dev/null -w '%{http_code}' https://localhost:5175/sw.js) || true
[[ "$STATUS" == "000" ]] || break
sleep 1
done

case "$STATUS" in
2*|3*)
echo "Web server ready on https://${STAGED_WEB_HOST:-$(hostname)}:5175"
;;
404)
echo "Web server is up but dist/ did not resolve — it is serving 404s." >&2
;;
*)
printf '%s\n' \
'Web server never came up on :5175. Release builds log nothing, so check:' \
' - STAGED_WEB_CERT_PATH / STAGED_WEB_KEY_PATH point at readable PEM files' \
' - no other Staged instance already holds the port' >&2
;;
esac

wait "$APP_PID" || true

# Build the app for production
build:
pnpm run tauri:build
Expand Down
1 change: 1 addition & 0 deletions apps/staged/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"dev:web": "vite --host 0.0.0.0",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-check --tsconfig ./tsconfig.app.json --fail-on-warnings && tsc -p tsconfig.node.json",
Expand Down
28 changes: 0 additions & 28 deletions apps/staged/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 3 additions & 6 deletions apps/staged/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,14 +65,9 @@ tauri-plugin-store = "2.4.2"
# MCP server for project sessions
rmcp = { version = "0.17", features = ["server", "transport-streamable-http-server"] }
axum = { version = "0.8", features = ["ws"] }
axum-extra = { version = "0.10", features = ["cookie"] }
tower-http = { version = "0.6", features = ["fs", "cors"] }
rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs", "std", "tls12"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["aws_lc_rs", "tls12"] }
rand = "0.9"
hex = "0.4"
subtle = "2.6"
time = "0.3"
pikchr = "0.1.4"

# Pikchr preview MCP tool: render Pikchr source to SVG (same C engine as the
Expand All @@ -81,9 +76,11 @@ resvg = "0.47"
usvg = "0.47"
tiny-skia = "0.12"

# Managed Node.js runtime: tarball extraction for runtime installs
# Managed Node.js runtime: tarball extraction and checksum verification for
# runtime installs
tar = "0.4"
flate2 = "1"
hex = "0.4"

[dev-dependencies]
# test-util: `#[tokio::test(start_paused = true)]` for the store-events
Expand Down
10 changes: 3 additions & 7 deletions apps/staged/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2158,21 +2158,17 @@ pub fn run() {
let (event_tx, _) = tokio::sync::broadcast::channel::<web_server::WebEvent>(256);
app.manage(event_tx.clone());

// Web server startup is stubbed out in this build.
// TODO(web): restore web server startup from the `mobile-web` branch.
// Start the Axum web server only when opted-in via environment variable.
// This avoids exposing an HTTP server on all interfaces for users who
// don't need browser-based access.
let web_server_enabled = std::env::var("STAGED_WEB_SERVER")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false);

if web_server_enabled {
let auth_token = web_server::generate_token();
web_server::start(web_server::WebAppState {
app_handle: app.handle().clone(),
event_tx,
auth_token,
sessions: std::sync::Arc::new(std::sync::Mutex::new(
std::collections::HashSet::new(),
)),
});
}

Expand Down
Loading