This commit is contained in:
adrianlat 2026-05-24 19:13:19 +06:00 committed by GitHub
commit 5eea18f7f4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 697 additions and 2 deletions

View File

@ -0,0 +1,93 @@
# MiroFish Hermes Integration Contract
## Purpose
This document defines the minimum stable contract for Hermes and OpenClaw to use the MiroFish host safely.
## Canonical Paths
- Runtime: `/Users/Shared/OpenClaw/mirofish-runtime`
- Transcribes: `/Users/Shared/OpenClaw/transcribes`
- Source only: `/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/airShare/MiroFish`
## Service Identity
- Service label: `com.openclaw.mirofish`
- Runtime owner: `airstride`
- Access model: LAN + SSH only
## Network Endpoints
- Frontend LAN URL: `http://10.0.0.161:3000`
- Backend LAN URL: `http://10.0.0.161:5001`
- Backend health: `http://10.0.0.161:5001/health`
- Frontend proxy health path: `http://10.0.0.161:3000/api/graph/project/list`
## SSH Access
- SSH alias: `openclaw-mirofish`
- SSH user: `airstride`
Recommended tunnels:
```bash
ssh -L 3000:127.0.0.1:3000 openclaw-mirofish
ssh -L 5001:127.0.0.1:5001 openclaw-mirofish
```
Tunnel-based local URLs:
- Frontend: `http://127.0.0.1:3000`
- Backend: `http://127.0.0.1:5001`
## Required Health Checks
Before Hermes or OpenClaw send operational work to MiroFish, the host should pass:
```bash
sudo launchctl print system/com.openclaw.mirofish
curl -sS http://127.0.0.1:5001/health
curl -sSI http://127.0.0.1:3000/
curl -sS http://127.0.0.1:3000/api/graph/project/list
```
Expected results:
- LaunchDaemon present in `system`
- Backend returns `{"service":"MiroFish Backend","status":"ok"}`
- Frontend returns `HTTP/1.1 200 OK`
- Frontend proxy returns a successful JSON payload
## Smoke Test
Use the canonical smoke test after reboot or maintenance:
```bash
/Users/Shared/OpenClaw/mirofish-runtime/scripts/host-smoke-test.sh
```
## Operational Data Rules
- Do not run MiroFish from iCloud.
- Do not write operational runtime state back into the iCloud source tree.
- Treat `/Users/Shared/OpenClaw/mirofish-runtime/backend/uploads` as persistent operational state.
- Treat `/Users/Shared/OpenClaw/transcribes` as the canonical local operational transcribes dataset.
## Consumption Rules
- Prefer SSH tunnels for operator access from other machines.
- Use LAN URLs only inside the trusted local network.
- Do not expose ports `3000` or `5001` through router forwarding or public internet ingress.
- Hermes and OpenClaw should assume MiroFish is a long-lived host service, not an ephemeral dev process.
## Operational Commands
From `/Users/Shared/OpenClaw/mirofish-runtime`:
```bash
./scripts/host-start.sh
./scripts/host-stop.sh
./scripts/host-status.sh
tail -f runtime/logs/backend.log
tail -f runtime/logs/frontend.log
```

View File

@ -0,0 +1,145 @@
# MiroFish Host Runbook
## Canonical Runtime
- Shared source of truth: `/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/airShare/MiroFish`
- Canonical operational runtime: `/Users/Shared/OpenClaw/mirofish-runtime`
- Canonical operational transcribes: `/Users/Shared/OpenClaw/transcribes`
The runtime should live outside iCloud because always-on services need stable local files, virtual environments, logs, and uploads that are not subject to iCloud eviction or placeholder behavior.
The iCloud source for breathwork transcribes remains:
- `/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/BreathWork/Transcribes`
That iCloud tree should be treated as source only. The shared operational copy should live in `/Users/Shared/OpenClaw/transcribes`.
## Ports
- Frontend: `3000`
- Backend: `5001`
## Commands
Run from the runtime root:
```bash
./scripts/host-start.sh
./scripts/host-stop.sh
./scripts/host-status.sh
tail -f runtime/logs/backend.log
tail -f runtime/logs/frontend.log
./scripts/host-smoke-test.sh
```
Sync transcribes from iCloud source to the shared operational path:
```bash
./scripts/sync-transcribes.sh
```
## SSH Tunnels
```bash
ssh -L 3000:127.0.0.1:3000 openclaw-mirofish
ssh -L 5001:127.0.0.1:5001 openclaw-mirofish
```
## Suggested SSH Alias
```sshconfig
Host openclaw-mirofish
HostName 10.0.0.161
User airstride
ServerAliveInterval 30
ServerAliveCountMax 3
StrictHostKeyChecking accept-new
```
## Legacy LaunchAgent Template
The current persistent service is the system LaunchDaemon below, running as `airstride`.
Use this LaunchAgent template only for a user-session fallback.
The validated plist is:
- `/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/airShare/MiroFish/ops/launchd/airshare.mirofish.plist`
It should be installed by a privileged shell or by a shell already running as `airstride`. If you are running as `airstride`, use:
```bash
cp /Users/adrianlat/Library/Mobile\ Documents/com~apple~CloudDocs/airShare/MiroFish/ops/launchd/airshare.mirofish.plist ~/Library/LaunchAgents/airshare.mirofish.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/airshare.mirofish.plist
launchctl kickstart -k gui/$(id -u)/airshare.mirofish
```
If you are running from another admin account, bootstrap into `airstride` requires root privileges or an interactive `airstride` session.
Template:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>airshare.mirofish</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/Shared/OpenClaw/mirofish-runtime/scripts/host-start.sh</string>
<string>/Users/Shared/OpenClaw/mirofish-runtime</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>AbandonProcessGroup</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime/runtime/logs/launchd.log</string>
<key>StandardErrorPath</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime/runtime/logs/launchd.err.log</string>
</dict>
</plist>
```
## Validated Constraints
- MiroFish is not run from iCloud.
- Runtime path is local and shared.
- SSH is LAN-only and should not be published through router/NAT.
- Docker was not available during validation, so source deployment is the active path.
## LaunchDaemon Migration
For a true always-on host, prefer the system LaunchDaemon:
- Source plist: `/Users/Shared/OpenClaw/mirofish-runtime/ops/launchd/com.openclaw.mirofish.plist`
- Installed plist: `/Library/LaunchDaemons/com.openclaw.mirofish.plist`
- Label: `com.openclaw.mirofish`
- User: `airstride`
Install with:
```bash
sudo /Users/Shared/OpenClaw/mirofish-runtime/scripts/install-mirofish-launchdaemon.sh
```
Validate with:
```bash
sudo launchctl print system/com.openclaw.mirofish
curl -sS http://127.0.0.1:5001/health
curl -sSI http://127.0.0.1:3000/
curl -sS http://127.0.0.1:3000/api/graph/project/list
```
## Hermes Integration
The canonical integration contract for Hermes and OpenClaw lives at:
- `/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/airShare/MiroFish/docs/hosting/mirofish-hermes-integration-contract.md`
- `/Users/Shared/OpenClaw/mirofish-runtime/docs/hosting/mirofish-hermes-integration-contract.md`

View File

@ -0,0 +1,47 @@
# MiroFish Ops Contract
## Canonical Paths
- Runtime: `/Users/Shared/OpenClaw/mirofish-runtime`
- Transcribes: `/Users/Shared/OpenClaw/transcribes`
- Source only: `/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/airShare/MiroFish`
## Ownership
- Service owner: `airstride`
- LaunchDaemon label: `com.openclaw.mirofish`
## Local Health Checks
```bash
sudo launchctl print system/com.openclaw.mirofish
curl -sS http://127.0.0.1:5001/health
curl -sSI http://127.0.0.1:3000/
curl -sS http://127.0.0.1:3000/api/graph/project/list
```
## Post-Reboot Smoke Test
```bash
/Users/Shared/OpenClaw/mirofish-runtime/scripts/host-smoke-test.sh
```
## LAN + SSH Access
- Frontend: `http://10.0.0.161:3000`
- Backend: `http://10.0.0.161:5001`
- SSH alias: `openclaw-mirofish`
- SSH user: `airstride`
Recommended tunnels:
```bash
ssh -L 3000:127.0.0.1:3000 openclaw-mirofish
ssh -L 5001:127.0.0.1:5001 openclaw-mirofish
```
## Operational Rule
- Do not run MiroFish from iCloud.
- Do not change canonical runtime or transcribes paths without migration.
- Treat `backend/uploads` and shared transcribes as operational state.

View File

@ -3,7 +3,7 @@ import i18n from '../i18n'
// 创建axios实例
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:5001',
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 300000, // 5分钟超时本体生成可能需要较长时间
headers: {
'Content-Type': 'application/json'

View File

@ -12,8 +12,20 @@ export default defineConfig({
}
},
server: {
host: '0.0.0.0',
port: 3000,
open: false,
proxy: {
'/api': {
target: 'http://localhost:5001',
changeOrigin: true,
secure: false
}
}
},
preview: {
host: '0.0.0.0',
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:5001',

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>airshare.mirofish</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/Shared/OpenClaw/mirofish-runtime/scripts/host-start.sh</string>
<string>/Users/Shared/OpenClaw/mirofish-runtime</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>AbandonProcessGroup</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime/runtime/logs/launchd.log</string>
<key>StandardErrorPath</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime/runtime/logs/launchd.err.log</string>
</dict>
</plist>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.openclaw.mirofish</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>/Users/Shared/OpenClaw/mirofish-runtime/scripts/host-start.sh</string>
<string>/Users/Shared/OpenClaw/mirofish-runtime</string>
</array>
<key>UserName</key>
<string>airstride</string>
<key>WorkingDirectory</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime</string>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/airstride</string>
<key>CODEX_HOME</key>
<string>/Users/airstride/.codex-openclaw</string>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
</dict>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>ProcessType</key>
<string>Background</string>
<key>AbandonProcessGroup</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime/runtime/logs/launchd.log</string>
<key>StandardErrorPath</key>
<string>/Users/Shared/OpenClaw/mirofish-runtime/runtime/logs/launchd.err.log</string>
<key>ThrottleInterval</key>
<integer>15</integer>
</dict>
</plist>

119
scripts/host-smoke-test.sh Executable file
View File

@ -0,0 +1,119 @@
#!/bin/zsh
set -euo pipefail
ROOT_DIR="${1:-/Users/Shared/OpenClaw/mirofish-runtime}"
TRANSCRIBES_DIR="${2:-/Users/Shared/OpenClaw/transcribes}"
DAEMON_LABEL="${3:-com.openclaw.mirofish}"
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
CURL_BIN="${CURL_BIN:-$(command -v curl)}"
LSOF_BIN="${LSOF_BIN:-$(command -v lsof)}"
NC_BIN="${NC_BIN:-$(command -v nc || true)}"
PLUTIL_BIN="${PLUTIL_BIN:-$(command -v plutil)}"
pass() {
printf '[PASS] %s\n' "$1"
}
fail() {
printf '[FAIL] %s\n' "$1" >&2
exit 1
}
run_with_optional_sudo() {
"$@" 2>/tmp/mirofish-command.err || sudo -n "$@" 2>/tmp/mirofish-command.err
}
check_port() {
local port="$1"
local output_file="/tmp/mirofish-port${port}.txt"
if run_with_optional_sudo "$LSOF_BIN" -nP "-iTCP:$port" -sTCP:LISTEN >"$output_file"; then
cat "$output_file"
pass "Port $port is listening"
return 0
fi
if [[ -n "$NC_BIN" ]] && "$NC_BIN" -z 127.0.0.1 "$port" >/dev/null 2>&1; then
pass "Port $port accepts TCP connections"
return 0
fi
cat /tmp/mirofish-command.err >&2 || true
fail "Port $port is not listening"
}
printf 'MiroFish smoke test\n'
printf 'runtime=%s\n' "$ROOT_DIR"
printf 'transcribes=%s\n' "$TRANSCRIBES_DIR"
printf 'daemon=%s\n\n' "$DAEMON_LABEL"
if run_with_optional_sudo launchctl print "system/$DAEMON_LABEL" >/tmp/mirofish-daemon.txt; then
sed -n '1,40p' /tmp/mirofish-daemon.txt
if grep -Eq 'state = (running|spawn scheduled)' /tmp/mirofish-daemon.txt; then
pass "LaunchDaemon is installed and active in launchd"
else
fail "LaunchDaemon is not active in launchd"
fi
else
cat /tmp/mirofish-command.err >&2 || true
fail "LaunchDaemon is not installed or not readable"
fi
if "$CURL_BIN" -fsS http://127.0.0.1:5001/health >/tmp/mirofish-backend.json; then
cat /tmp/mirofish-backend.json
pass "Backend health endpoint responds"
else
fail "Backend health endpoint failed"
fi
if "$CURL_BIN" -fsSI http://127.0.0.1:3000/ >/tmp/mirofish-frontend.headers; then
sed -n '1,8p' /tmp/mirofish-frontend.headers
pass "Frontend root responds"
else
fail "Frontend root failed"
fi
if "$CURL_BIN" -fsS http://127.0.0.1:3000/api/graph/project/list >/tmp/mirofish-proxy.json; then
cat /tmp/mirofish-proxy.json
pass "Frontend proxy to backend responds"
else
fail "Frontend proxy failed"
fi
check_port 3000
check_port 5001
if [[ -r "$ROOT_DIR/runtime/logs/backend.log" && -r "$ROOT_DIR/runtime/logs/frontend.log" ]]; then
tail -n 5 "$ROOT_DIR/runtime/logs/backend.log" || true
printf '\n'
tail -n 5 "$ROOT_DIR/runtime/logs/frontend.log" || true
pass "Logs are present"
else
fail "Runtime logs are missing"
fi
if [[ -d "$TRANSCRIBES_DIR" ]]; then
transcribe_count="$(find "$TRANSCRIBES_DIR" -type f 2>/dev/null | wc -l | tr -d ' ')"
transcribe_size="$(du -sh "$TRANSCRIBES_DIR" 2>/dev/null | awk '{print $1}')"
printf 'transcribes_files=%s\n' "$transcribe_count"
printf 'transcribes_size=%s\n' "$transcribe_size"
if [[ "$transcribe_count" -gt 0 ]]; then
pass "Transcribes path is populated"
else
fail "Transcribes path is empty"
fi
else
fail "Transcribes path does not exist"
fi
if [[ -f "/Library/LaunchDaemons/$DAEMON_LABEL.plist" ]]; then
"$PLUTIL_BIN" -lint "/Library/LaunchDaemons/$DAEMON_LABEL.plist"
pass "Installed LaunchDaemon plist is valid"
else
fail "Installed LaunchDaemon plist is missing"
fi
printf '\nMiroFish smoke test completed successfully.\n'

86
scripts/host-start.sh Executable file
View File

@ -0,0 +1,86 @@
#!/bin/zsh
set -euo pipefail
ROOT_DIR="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
LOG_DIR="$ROOT_DIR/runtime/logs"
PID_DIR="$ROOT_DIR/runtime/pids"
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
UV_BIN="${UV_BIN:-$(command -v uv)}"
NPM_BIN="${NPM_BIN:-$(command -v npm)}"
NPX_BIN="${NPX_BIN:-$(command -v npx)}"
CURL_BIN="${CURL_BIN:-$(command -v curl)}"
LSOF_BIN="${LSOF_BIN:-$(command -v lsof)}"
mkdir -p "$LOG_DIR" "$PID_DIR" "$ROOT_DIR/backend/uploads/reports" "$ROOT_DIR/backend/uploads/simulations"
monitor_existing_runtime() {
echo "MiroFish already running"
while true; do
if ! "$CURL_BIN" -fsS http://127.0.0.1:5001/health >/dev/null 2>&1; then
echo "Backend health check failed"
exit 1
fi
if ! "$CURL_BIN" -fsS http://127.0.0.1:3000/ >/dev/null 2>&1; then
echo "Frontend health check failed"
exit 1
fi
sleep 30
done
}
if "$LSOF_BIN" -nP -iTCP:3000 -sTCP:LISTEN >/dev/null 2>&1; then
if "$CURL_BIN" -fsS http://127.0.0.1:3000/ >/dev/null 2>&1 && \
"$CURL_BIN" -fsS http://127.0.0.1:5001/health >/dev/null 2>&1; then
monitor_existing_runtime
fi
echo "Port 3000 already in use by an unhealthy process"
exit 1
fi
if "$LSOF_BIN" -nP -iTCP:5001 -sTCP:LISTEN >/dev/null 2>&1; then
if "$CURL_BIN" -fsS http://127.0.0.1:3000/ >/dev/null 2>&1 && \
"$CURL_BIN" -fsS http://127.0.0.1:5001/health >/dev/null 2>&1; then
monitor_existing_runtime
fi
echo "Port 5001 already in use by an unhealthy process"
exit 1
fi
cd "$ROOT_DIR/backend"
env FLASK_DEBUG=False PYTHONUNBUFFERED=1 "$UV_BIN" run --no-sync python run.py < /dev/null > "$LOG_DIR/backend.log" 2>&1 &
BACKEND_PID=$!
echo "$BACKEND_PID" > "$PID_DIR/backend.pid"
for _ in {1..30}; do
if "$CURL_BIN" -fsS http://127.0.0.1:5001/health >/dev/null 2>&1; then
break
fi
sleep 1
done
if ! "$CURL_BIN" -fsS http://127.0.0.1:5001/health >/dev/null 2>&1; then
echo "Backend failed to start"
exit 1
fi
cd "$ROOT_DIR/frontend"
"$NPM_BIN" run build >/dev/null
"$NPX_BIN" vite preview --host 0.0.0.0 --port 3000 --strictPort < /dev/null > "$LOG_DIR/frontend.log" 2>&1 &
FRONTEND_PID=$!
echo "$FRONTEND_PID" > "$PID_DIR/frontend.pid"
for _ in {1..30}; do
if "$CURL_BIN" -fsS http://127.0.0.1:3000/ >/dev/null 2>&1; then
break
fi
sleep 1
done
if ! "$CURL_BIN" -fsS http://127.0.0.1:3000/ >/dev/null 2>&1; then
echo "Frontend failed to start"
exit 1
fi
echo "MiroFish started"
wait "$BACKEND_PID" "$FRONTEND_PID"

23
scripts/host-status.sh Executable file
View File

@ -0,0 +1,23 @@
#!/bin/zsh
set -euo pipefail
ROOT_DIR="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
PID_DIR="$ROOT_DIR/runtime/pids"
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
CURL_BIN="${CURL_BIN:-$(command -v curl)}"
LSOF_BIN="${LSOF_BIN:-$(command -v lsof)}"
echo "Backend health:"
"$CURL_BIN" -fsS http://127.0.0.1:5001/health || true
echo
echo
echo "Frontend headers:"
"$CURL_BIN" -fsSI http://127.0.0.1:3000/ || true
echo
echo "Ports:"
"$LSOF_BIN" -nP -iTCP:3000 -sTCP:LISTEN || true
"$LSOF_BIN" -nP -iTCP:5001 -sTCP:LISTEN || true
echo
echo "PID files:"
ls -la "$PID_DIR" 2>/dev/null || true

35
scripts/host-stop.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/zsh
set -euo pipefail
ROOT_DIR="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
PID_DIR="$ROOT_DIR/runtime/pids"
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
PKILL_BIN="${PKILL_BIN:-$(command -v pkill)}"
stop_pid_file() {
local pid_file="$1"
if [[ -f "$pid_file" ]]; then
local pid
pid="$(cat "$pid_file")"
if kill -0 "$pid" >/dev/null 2>&1; then
kill "$pid" >/dev/null 2>&1 || true
for _ in {1..20}; do
if ! kill -0 "$pid" >/dev/null 2>&1; then
break
fi
sleep 1
done
kill -9 "$pid" >/dev/null 2>&1 || true
fi
rm -f "$pid_file"
fi
}
stop_pid_file "$PID_DIR/frontend.pid"
stop_pid_file "$PID_DIR/backend.pid"
"$PKILL_BIN" -f "vite preview --host 0.0.0.0 --port 3000" >/dev/null 2>&1 || true
"$PKILL_BIN" -f "uv run --no-sync python run.py" >/dev/null 2>&1 || true
echo "MiroFish stopped"

View File

@ -0,0 +1,27 @@
#!/bin/zsh
set -euo pipefail
PLIST_SOURCE="/Users/Shared/OpenClaw/mirofish-runtime/ops/launchd/com.openclaw.mirofish.plist"
PLIST_TARGET="/Library/LaunchDaemons/com.openclaw.mirofish.plist"
if [[ ! -f "$PLIST_SOURCE" ]]; then
echo "Missing source plist: $PLIST_SOURCE"
exit 1
fi
if [[ "$(id -u)" -ne 0 ]]; then
echo "Run this script with sudo."
exit 1
fi
cp "$PLIST_SOURCE" "$PLIST_TARGET"
chown root:wheel "$PLIST_TARGET"
chmod 644 "$PLIST_TARGET"
plutil -lint "$PLIST_TARGET"
launchctl bootout system/com.openclaw.mirofish 2>/dev/null || true
launchctl bootstrap system "$PLIST_TARGET"
launchctl kickstart -k system/com.openclaw.mirofish
echo "Installed LaunchDaemon at $PLIST_TARGET"

View File

@ -0,0 +1,27 @@
#!/bin/zsh
set -euo pipefail
if [[ "$(id -un)" != "airstride" ]]; then
echo "Run this script as airstride."
exit 1
fi
RUNTIME_ROOT="/Users/Shared/OpenClaw/mirofish-runtime"
PLIST_SOURCE="$RUNTIME_ROOT/ops/launchd/airshare.mirofish.plist"
PLIST_TARGET="$HOME/Library/LaunchAgents/airshare.mirofish.plist"
mkdir -p "$HOME/Library/LaunchAgents"
cp "$PLIST_SOURCE" "$PLIST_TARGET"
if launchctl print "gui/$(id -u)" >/dev/null 2>&1; then
DOMAIN="gui/$(id -u)"
else
DOMAIN="user/$(id -u)"
fi
launchctl bootout "$DOMAIN/airshare.mirofish" 2>/dev/null || true
launchctl bootstrap "$DOMAIN" "$PLIST_TARGET"
launchctl kickstart -k "$DOMAIN/airshare.mirofish"
echo "Installed LaunchAgent at $PLIST_TARGET in $DOMAIN"

14
scripts/sync-transcribes.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/zsh
set -euo pipefail
SOURCE_DIR="${1:-/Users/adrianlat/Library/Mobile Documents/com~apple~CloudDocs/BreathWork/Transcribes}"
TARGET_DIR="${2:-/Users/Shared/OpenClaw/transcribes}"
mkdir -p "$TARGET_DIR"
rsync -a --delete --human-readable "$SOURCE_DIR/" "$TARGET_DIR/"
chgrp -R openclawshare "$TARGET_DIR"
chmod -R g+rwX "$TARGET_DIR"
find "$TARGET_DIR" -type d -exec chmod g+s {} +
echo "Transcribes synced to $TARGET_DIR"