Merge 78a17c824b into db1bc144ff
This commit is contained in:
commit
5eea18f7f4
|
|
@ -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
|
||||||
|
```
|
||||||
|
|
@ -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`
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -3,7 +3,7 @@ import i18n from '../i18n'
|
||||||
|
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service = axios.create({
|
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分钟超时(本体生成可能需要较长时间)
|
timeout: 300000, // 5分钟超时(本体生成可能需要较长时间)
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,20 @@ export default defineConfig({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
server: {
|
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,
|
port: 3000,
|
||||||
open: true,
|
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': {
|
'/api': {
|
||||||
target: 'http://localhost:5001',
|
target: 'http://localhost:5001',
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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'
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -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"
|
||||||
Loading…
Reference in New Issue