feat(phase5): polish + deployment
- skill/SKILL.md: rewritten to 9 lines — 'status is automatic' - deploy-to-agents.sh: no AGENTS.md injection; deploys hook + npm install - install.sh: clean install flow; prints required env vars - deploy/status-watcher.service: systemd unit file - deploy/Dockerfile: containerized deployment (node:22-alpine) - src/live-status.js: deprecation warning + start-watcher/stop-watcher pass-through - README.md: full docs (architecture, install, config, upgrade guide, troubleshooting) - make check: 0 errors, 0 format issues - npm test: 59 unit + 36 integration = 95 tests passing
This commit is contained in:
262
README.md
262
README.md
@@ -1,28 +1,256 @@
|
|||||||
# OpenClaw Live Status Tool
|
# Live Status v4
|
||||||
|
|
||||||
A lightweight CLI tool for OpenClaw agents to provide "Antigravity-style" live status updates in Mattermost channels (and others) without spamming.
|
Real-time Mattermost progress updates for OpenClaw agent sessions.
|
||||||
|
|
||||||
## Features
|
Version 4 replaces the manual v1 live-status CLI with a transparent infrastructure daemon.
|
||||||
|
Agents no longer need to call `live-status`. The watcher auto-updates Mattermost as they work.
|
||||||
|
|
||||||
- **Live Updates:** Create a single message and update it repeatedly.
|
## Architecture
|
||||||
- **Sub-Agent Support:** Works in clean environments via embedded config or CLI flags.
|
|
||||||
- **Cross-Channel:** Supports dynamic channel targeting via `--channel`.
|
|
||||||
- **One-Click Install:** Updates binaries, skills, and agent protocols automatically.
|
|
||||||
|
|
||||||
## Installation
|
```
|
||||||
|
OpenClaw Gateway
|
||||||
|
Agent Sessions
|
||||||
|
-> writes {uuid}.jsonl as they run
|
||||||
|
|
||||||
Run the interactive installer wizard:
|
status-watcher daemon (SINGLE PROCESS)
|
||||||
|
-> fs.watch recursive on transcript directory (inotify, Node 22)
|
||||||
|
-> Multiplexes all active sessions
|
||||||
|
-> SessionState map: sessionKey -> { postId, pendingToolCalls, lines[] }
|
||||||
|
-> Shared HTTP connection pool (keep-alive, maxSockets=4)
|
||||||
|
-> Throttled Mattermost updates (leading edge + trailing flush, 500ms)
|
||||||
|
-> Circuit breaker for API failure resilience
|
||||||
|
-> Graceful shutdown (SIGTERM -> mark all boxes "interrupted")
|
||||||
|
-> Sub-agent nesting (child sessions under parent status box)
|
||||||
|
|
||||||
```bash
|
gateway:startup hook
|
||||||
./install.sh
|
-> hooks/status-watcher-hook/handler.js
|
||||||
|
-> Checks PID file; spawns daemon if not running
|
||||||
|
-> Daemon starts automatically with gateway
|
||||||
|
|
||||||
|
Mattermost API
|
||||||
|
-> PUT /api/v4/posts/{id} (in-place edits, unlimited)
|
||||||
|
-> Shared http.Agent (keepAlive, maxSockets=4)
|
||||||
|
-> Circuit breaker: open after 5 failures, 30s cooldown
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Install
|
||||||
|
|
||||||
```bash
|
### Prerequisites
|
||||||
# Create a new status box
|
|
||||||
ID=$(live-status create "Initializing...")
|
|
||||||
|
|
||||||
# Update the status box
|
- Node.js 22.x
|
||||||
live-status update $ID "Working..."
|
- OpenClaw gateway running
|
||||||
|
- Mattermost bot token
|
||||||
|
|
||||||
|
### One-command install
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /path/to/MATTERMOST_OPENCLAW_LIVESTATUS
|
||||||
|
bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This installs npm dependencies and deploys the `gateway:startup` hook.
|
||||||
|
The daemon starts automatically on the next gateway restart.
|
||||||
|
|
||||||
|
### Manual start (without gateway restart)
|
||||||
|
|
||||||
|
Set required env vars, then:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node src/watcher-manager.js start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
All config via environment variables. No hardcoded values.
|
||||||
|
|
||||||
|
### Required
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
| ---------------- | ----------------------------------------------------- |
|
||||||
|
| `MM_TOKEN` | Mattermost bot token |
|
||||||
|
| `MM_URL` | Mattermost base URL (e.g. `https://slack.solio.tech`) |
|
||||||
|
| `TRANSCRIPT_DIR` | Path to agent sessions directory |
|
||||||
|
| `SESSIONS_JSON` | Path to sessions.json |
|
||||||
|
|
||||||
|
### Optional
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
| ---------------------------- | ------------------------- | ------------------------------------------ |
|
||||||
|
| `THROTTLE_MS` | `500` | Min interval between Mattermost updates |
|
||||||
|
| `IDLE_TIMEOUT_S` | `60` | Inactivity before marking session complete |
|
||||||
|
| `MAX_SESSION_DURATION_S` | `1800` | Hard timeout per session (30 min) |
|
||||||
|
| `MAX_STATUS_LINES` | `15` | Max lines in status box (oldest dropped) |
|
||||||
|
| `MAX_ACTIVE_SESSIONS` | `20` | Concurrent status box limit |
|
||||||
|
| `MAX_MESSAGE_CHARS` | `15000` | Mattermost post truncation limit |
|
||||||
|
| `HEALTH_PORT` | `9090` | Health endpoint port (0 = disabled) |
|
||||||
|
| `LOG_LEVEL` | `info` | Logging level (pino) |
|
||||||
|
| `PID_FILE` | `/tmp/status-watcher.pid` | PID file location |
|
||||||
|
| `CIRCUIT_BREAKER_THRESHOLD` | `5` | Failures before circuit opens |
|
||||||
|
| `CIRCUIT_BREAKER_COOLDOWN_S` | `30` | Cooldown before half-open probe |
|
||||||
|
| `TOOL_LABELS_FILE` | _(built-in)_ | External tool labels JSON override |
|
||||||
|
| `DEFAULT_CHANNEL` | _none_ | Fallback channel for non-MM sessions |
|
||||||
|
|
||||||
|
## Status Box Format
|
||||||
|
|
||||||
|
```
|
||||||
|
[ACTIVE] main | 38s
|
||||||
|
Reading live-status source code...
|
||||||
|
exec: ls /agents/sessions [OK]
|
||||||
|
Analyzing agent configurations...
|
||||||
|
exec: grep -r live-status [OK]
|
||||||
|
Writing new implementation...
|
||||||
|
Sub-agent: proj035-planner
|
||||||
|
Reading protocol...
|
||||||
|
Analyzing JSONL format...
|
||||||
|
[DONE] 28s
|
||||||
|
Plan ready. Awaiting approval.
|
||||||
|
[DONE] 53s | 12.4k tokens
|
||||||
|
```
|
||||||
|
|
||||||
|
## Daemon Management
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Start
|
||||||
|
node src/watcher-manager.js start
|
||||||
|
|
||||||
|
# Stop (graceful shutdown)
|
||||||
|
node src/watcher-manager.js stop
|
||||||
|
|
||||||
|
# Status
|
||||||
|
node src/watcher-manager.js status
|
||||||
|
|
||||||
|
# Pass-through via legacy CLI
|
||||||
|
live-status start-watcher
|
||||||
|
live-status stop-watcher
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
curl http://localhost:9090/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Deployment Options
|
||||||
|
|
||||||
|
### Hook (default)
|
||||||
|
|
||||||
|
The `gateway:startup` hook in `hooks/status-watcher-hook/` auto-starts the daemon.
|
||||||
|
No configuration needed beyond deploying the hook.
|
||||||
|
|
||||||
|
### systemd
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Copy service file
|
||||||
|
cp deploy/status-watcher.service /etc/systemd/system/
|
||||||
|
|
||||||
|
# Create env file
|
||||||
|
cat > /etc/status-watcher.env <<EOF
|
||||||
|
MM_TOKEN=your_token
|
||||||
|
MM_URL=https://slack.solio.tech
|
||||||
|
TRANSCRIPT_DIR=/home/node/.openclaw/agents/main/sessions
|
||||||
|
SESSIONS_JSON=/home/node/.openclaw/agents/main/sessions/sessions.json
|
||||||
|
EOF
|
||||||
|
|
||||||
|
systemctl enable --now status-watcher
|
||||||
|
```
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker build -f deploy/Dockerfile -t status-watcher .
|
||||||
|
docker run -d \
|
||||||
|
-e MM_TOKEN=your_token \
|
||||||
|
-e MM_URL=https://slack.solio.tech \
|
||||||
|
-e TRANSCRIPT_DIR=/sessions \
|
||||||
|
-e SESSIONS_JSON=/sessions/sessions.json \
|
||||||
|
-v /home/node/.openclaw/agents:/sessions:ro \
|
||||||
|
-p 9090:9090 \
|
||||||
|
status-watcher
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upgrade from v1
|
||||||
|
|
||||||
|
v1 required agents to call `live-status create/update/complete` manually.
|
||||||
|
AGENTS.md contained a large "Live Status Protocol (MANDATORY)" section.
|
||||||
|
|
||||||
|
### What changes
|
||||||
|
|
||||||
|
1. The daemon handles all updates — no manual calls needed.
|
||||||
|
2. AGENTS.md protocol section can be removed (see `docs/v1-removal-checklist.md`).
|
||||||
|
3. `skill/SKILL.md` is now 9 lines: "status is automatic".
|
||||||
|
4. `live-status` CLI still works for manual use but prints a deprecation notice.
|
||||||
|
|
||||||
|
### Migration steps
|
||||||
|
|
||||||
|
1. Run `bash install.sh` to deploy v4.
|
||||||
|
2. Restart the gateway (hook activates).
|
||||||
|
3. Verify the daemon is running: `curl localhost:9090/health`
|
||||||
|
4. After 1+ hours of verified operation, remove the v1 AGENTS.md sections
|
||||||
|
(see `docs/v1-removal-checklist.md` for exact sections to remove).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Daemon not starting:**
|
||||||
|
|
||||||
|
- Check PID file: `cat /tmp/status-watcher.pid`
|
||||||
|
- Check env vars: `MM_TOKEN`, `MM_URL`, `TRANSCRIPT_DIR`, `SESSIONS_JSON` must all be set
|
||||||
|
- Start manually and check logs: `node src/watcher-manager.js start`
|
||||||
|
|
||||||
|
**No status updates appearing:**
|
||||||
|
|
||||||
|
- Check health endpoint: `curl localhost:9090/health`
|
||||||
|
- Check circuit breaker state (shown in health response)
|
||||||
|
- Verify `MM_TOKEN` has permission to post in the target channel
|
||||||
|
|
||||||
|
**Duplicate status boxes:**
|
||||||
|
|
||||||
|
- Multiple daemon instances — check PID file, kill extras
|
||||||
|
- `node src/watcher-manager.js status` shows if it's running
|
||||||
|
|
||||||
|
**Session compaction:**
|
||||||
|
|
||||||
|
- When JSONL is truncated, the watcher detects it (stat.size < lastOffset)
|
||||||
|
- Offset resets, status box shows `[session compacted - continuing]`
|
||||||
|
- No crash, no data loss
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Run all tests
|
||||||
|
npm test
|
||||||
|
|
||||||
|
# Run unit tests only
|
||||||
|
npm run test-unit
|
||||||
|
|
||||||
|
# Run integration tests only
|
||||||
|
npm run test-integration
|
||||||
|
|
||||||
|
# Lint + format + test
|
||||||
|
make check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
watcher-manager.js Entrypoint; PID file; graceful shutdown
|
||||||
|
status-watcher.js JSONL file watcher (inotify)
|
||||||
|
session-monitor.js sessions.json poller (2s interval)
|
||||||
|
status-box.js Mattermost post manager (throttle, circuit breaker)
|
||||||
|
status-formatter.js Status box text renderer
|
||||||
|
circuit-breaker.js Circuit breaker state machine
|
||||||
|
config.js Env var config with validation
|
||||||
|
logger.js pino wrapper
|
||||||
|
health.js HTTP health endpoint
|
||||||
|
tool-labels.js Tool name -> label resolver
|
||||||
|
tool-labels.json Built-in tool label defaults
|
||||||
|
live-status.js Legacy CLI (deprecated; backward compat)
|
||||||
|
|
||||||
|
hooks/
|
||||||
|
status-watcher-hook/ gateway:startup hook (auto-start daemon)
|
||||||
|
|
||||||
|
deploy/
|
||||||
|
status-watcher.service systemd unit file
|
||||||
|
Dockerfile Container deployment
|
||||||
|
|
||||||
|
test/
|
||||||
|
unit/ Unit tests (59 tests)
|
||||||
|
integration/ Integration tests (36 tests)
|
||||||
```
|
```
|
||||||
|
|||||||
53
deploy-to-agents.sh
Executable file
53
deploy-to-agents.sh
Executable file
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# deploy-to-agents.sh — Deploy Live Status v4 to OpenClaw workspace
|
||||||
|
#
|
||||||
|
# Deploys the gateway:startup hook and installs npm dependencies.
|
||||||
|
# Does NOT inject anything into AGENTS.md.
|
||||||
|
# The watcher daemon auto-starts on next gateway restart.
|
||||||
|
#
|
||||||
|
# Usage: bash deploy-to-agents.sh [--workspace DIR]
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
WORKSPACE="${WORKSPACE:-/home/node/.openclaw/workspace}"
|
||||||
|
|
||||||
|
# Parse flags
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--workspace) WORKSPACE="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "==================================="
|
||||||
|
echo " Live Status v4 Deploy"
|
||||||
|
echo "==================================="
|
||||||
|
echo "Workspace: $WORKSPACE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 1. npm install
|
||||||
|
echo "[1/2] Installing npm dependencies..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
npm install --production
|
||||||
|
echo " Done."
|
||||||
|
|
||||||
|
# 2. Deploy hook
|
||||||
|
echo "[2/2] Deploying gateway:startup hook..."
|
||||||
|
HOOKS_DIR="$WORKSPACE/hooks"
|
||||||
|
mkdir -p "$HOOKS_DIR/status-watcher-hook"
|
||||||
|
cp -r "$SCRIPT_DIR/hooks/status-watcher-hook/." "$HOOKS_DIR/status-watcher-hook/"
|
||||||
|
echo " Hook deployed to: $HOOKS_DIR/status-watcher-hook/"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==================================="
|
||||||
|
echo " Deployment Complete"
|
||||||
|
echo "==================================="
|
||||||
|
echo ""
|
||||||
|
echo "The watcher will auto-start on next gateway startup."
|
||||||
|
echo ""
|
||||||
|
echo "To activate immediately, ensure these env vars are set, then run:"
|
||||||
|
echo " node $SCRIPT_DIR/src/watcher-manager.js start"
|
||||||
|
echo ""
|
||||||
|
echo "Required env vars: MM_TOKEN, MM_URL, TRANSCRIPT_DIR, SESSIONS_JSON"
|
||||||
|
echo "See install.sh for full config reference."
|
||||||
36
deploy/Dockerfile
Normal file
36
deploy/Dockerfile
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
FROM node:22-alpine
|
||||||
|
|
||||||
|
LABEL description="Live Status v4 - OpenClaw session watcher daemon"
|
||||||
|
LABEL source="https://git.eeqj.de/ROOH/MATTERMOST_OPENCLAW_LIVESTATUS"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files and install production dependencies only
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci --production
|
||||||
|
|
||||||
|
# Copy source and supporting files
|
||||||
|
COPY src/ ./src/
|
||||||
|
COPY skill/ ./skill/
|
||||||
|
|
||||||
|
# Environment variables (required — set at runtime)
|
||||||
|
# MM_TOKEN, MM_URL, TRANSCRIPT_DIR, SESSIONS_JSON must be provided
|
||||||
|
ENV NODE_ENV=production \
|
||||||
|
LOG_LEVEL=info \
|
||||||
|
HEALTH_PORT=9090 \
|
||||||
|
THROTTLE_MS=500 \
|
||||||
|
IDLE_TIMEOUT_S=60 \
|
||||||
|
MAX_STATUS_LINES=15 \
|
||||||
|
MAX_ACTIVE_SESSIONS=20 \
|
||||||
|
PID_FILE=/tmp/status-watcher.pid
|
||||||
|
|
||||||
|
# Health check
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
||||||
|
CMD wget -qO- http://localhost:${HEALTH_PORT}/health || exit 1
|
||||||
|
|
||||||
|
# Run as non-root
|
||||||
|
USER node
|
||||||
|
|
||||||
|
EXPOSE ${HEALTH_PORT}
|
||||||
|
|
||||||
|
CMD ["node", "src/watcher-manager.js", "start"]
|
||||||
36
deploy/status-watcher.service
Normal file
36
deploy/status-watcher.service
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Live Status v4 - OpenClaw session watcher daemon
|
||||||
|
Documentation=https://git.eeqj.de/ROOH/MATTERMOST_OPENCLAW_LIVESTATUS
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=node
|
||||||
|
WorkingDirectory=/opt/openclaw-live-status
|
||||||
|
|
||||||
|
# Load environment variables from file
|
||||||
|
EnvironmentFile=/etc/status-watcher.env
|
||||||
|
|
||||||
|
# Start the watcher daemon directly (not via CLI wrapper)
|
||||||
|
ExecStart=/usr/bin/node /opt/openclaw-live-status/src/watcher-manager.js start
|
||||||
|
|
||||||
|
# Graceful shutdown — watcher handles SIGTERM (marks boxes interrupted, flushes)
|
||||||
|
ExecStop=/bin/kill -TERM $MAINPID
|
||||||
|
|
||||||
|
# Restart policy
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5s
|
||||||
|
StartLimitBurst=3
|
||||||
|
StartLimitIntervalSec=60s
|
||||||
|
|
||||||
|
# Logging — output goes to journald
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=status-watcher
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges=true
|
||||||
|
PrivateTmp=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
134
install.sh
134
install.sh
@@ -1,69 +1,81 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# install.sh — Live Status v4 installer
|
||||||
|
#
|
||||||
|
# Installs npm dependencies and deploys the gateway:startup hook.
|
||||||
|
# The watcher daemon starts automatically on next gateway restart.
|
||||||
|
#
|
||||||
|
# Usage: bash install.sh [--workspace DIR]
|
||||||
|
|
||||||
# OpenClaw Live Status Installer (Wizard Mode)
|
set -e
|
||||||
|
|
||||||
echo "=========================================="
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
echo " OpenClaw Live Status Tool - Installer "
|
WORKSPACE="${1:-/home/node/.openclaw/workspace}"
|
||||||
echo "=========================================="
|
|
||||||
|
|
||||||
# 1. Install Binary
|
# Parse flags
|
||||||
echo "[+] Installing binary to /usr/local/bin/live-status..."
|
while [[ "$#" -gt 0 ]]; do
|
||||||
cp src/live-status.js /usr/local/bin/live-status
|
case "$1" in
|
||||||
chmod +x /usr/local/bin/live-status
|
--workspace) WORKSPACE="$2"; shift 2 ;;
|
||||||
|
*) shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# 2. Install Skill
|
echo "==================================="
|
||||||
echo "[+] Installing skill to /app/skills/live-status..."
|
echo " Live Status v4 Installer"
|
||||||
mkdir -p /app/skills/live-status
|
echo "==================================="
|
||||||
cp skill/SKILL.md /app/skills/live-status/SKILL.md
|
echo "Project: $SCRIPT_DIR"
|
||||||
|
echo "Workspace: $WORKSPACE"
|
||||||
# 3. Global Agent Configuration (Injection)
|
|
||||||
echo "[+] Configuring global agent defaults..."
|
|
||||||
AGENTS_FILE="/home/node/.openclaw/workspace/AGENTS.md"
|
|
||||||
|
|
||||||
if [ -f "$AGENTS_FILE" ]; then
|
|
||||||
if grep -q "Live Status Protocol" "$AGENTS_FILE"; then
|
|
||||||
echo " > AGENTS.md already configured."
|
|
||||||
else
|
|
||||||
echo " > Injecting mandatory protocol into AGENTS.md..."
|
|
||||||
cat >> "$AGENTS_FILE" <<EOF
|
|
||||||
|
|
||||||
## 📡 Live Status Protocol (MANDATORY)
|
|
||||||
|
|
||||||
**For ANY multi-step task (e.g., research, downloading, installing, compiling):**
|
|
||||||
|
|
||||||
1. **Read Skill:** Load \`/app/skills/live-status/SKILL.md\`.
|
|
||||||
2. **Initialize:** Run \`live-status create\` with a code block (\`[STATUS] INITIALIZING...\`).
|
|
||||||
3. **Capture ID:** Store the Post ID returned by \`create\`.
|
|
||||||
4. **Update:** Frequently run \`live-status update <ID>\` to stream progress.
|
|
||||||
5. **Complete:** Mark as \`[COMPLETED]\` when done.
|
|
||||||
|
|
||||||
**Why:** This provides "Antigravity-style" visibility without spamming channels. Sub-agents MUST use this tool instead of \`message\` for progress reports.
|
|
||||||
EOF
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo " > Warning: AGENTS.md not found. Skipping injection."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4. Mattermost Configuration (Wizard)
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "[?] Mattermost Configuration Check:"
|
|
||||||
# Check if the installed tool still has the default placeholder
|
|
||||||
if grep -q "DEFAULT_TOKEN_PLACEHOLDER" /usr/local/bin/live-status; then
|
|
||||||
echo " > Default token detected."
|
|
||||||
read -p " > Enter your Mattermost Bot Token: " NEW_TOKEN
|
|
||||||
if [[ -n "$NEW_TOKEN" ]]; then
|
|
||||||
# Replace the placeholder in the INSTALLED binary (not the source)
|
|
||||||
sed -i "s/DEFAULT_TOKEN_PLACEHOLDER/$NEW_TOKEN/g" /usr/local/bin/live-status
|
|
||||||
echo " > Token configured successfully."
|
|
||||||
else
|
|
||||||
echo " > No token entered. Tool may not function."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo " > Custom token already configured."
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
# 1. Install npm dependencies
|
||||||
|
echo "[1/3] Installing npm dependencies..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
npm install --production
|
||||||
|
echo " Done."
|
||||||
|
|
||||||
|
# 2. Deploy hook
|
||||||
|
echo "[2/3] Deploying gateway:startup hook..."
|
||||||
|
HOOKS_DIR="$WORKSPACE/hooks"
|
||||||
|
mkdir -p "$HOOKS_DIR/status-watcher-hook"
|
||||||
|
cp -r "$SCRIPT_DIR/hooks/status-watcher-hook/." "$HOOKS_DIR/status-watcher-hook/"
|
||||||
|
echo " Hook deployed to: $HOOKS_DIR/status-watcher-hook/"
|
||||||
|
|
||||||
|
# 3. Print required environment variables
|
||||||
|
echo "[3/3] Post-install configuration"
|
||||||
echo ""
|
echo ""
|
||||||
echo "=========================================="
|
echo "==================================="
|
||||||
echo " Installation Complete!"
|
echo " Required Environment Variables"
|
||||||
echo "=========================================="
|
echo "==================================="
|
||||||
echo "Usage: live-status create \"...\""
|
echo ""
|
||||||
|
echo "Set these before the watcher will function:"
|
||||||
|
echo ""
|
||||||
|
echo " MM_TOKEN Mattermost bot token"
|
||||||
|
echo " (find in openclaw.json -> mattermost.accounts)"
|
||||||
|
echo ""
|
||||||
|
echo " MM_URL Mattermost base URL"
|
||||||
|
echo " e.g. https://slack.solio.tech"
|
||||||
|
echo ""
|
||||||
|
echo " TRANSCRIPT_DIR Path to agent sessions directory"
|
||||||
|
echo " e.g. /home/node/.openclaw/agents/main/sessions"
|
||||||
|
echo ""
|
||||||
|
echo " SESSIONS_JSON Path to sessions.json"
|
||||||
|
echo " e.g. /home/node/.openclaw/agents/main/sessions/sessions.json"
|
||||||
|
echo ""
|
||||||
|
echo "Optional (shown with defaults):"
|
||||||
|
echo " THROTTLE_MS=500 Update interval (ms)"
|
||||||
|
echo " IDLE_TIMEOUT_S=60 Idle before marking session done"
|
||||||
|
echo " MAX_STATUS_LINES=15 Lines shown in status box"
|
||||||
|
echo " MAX_ACTIVE_SESSIONS=20 Concurrent session limit"
|
||||||
|
echo " HEALTH_PORT=9090 Health endpoint port (0=disabled)"
|
||||||
|
echo " LOG_LEVEL=info Logging level"
|
||||||
|
echo " PID_FILE=/tmp/status-watcher.pid"
|
||||||
|
echo ""
|
||||||
|
echo "==================================="
|
||||||
|
echo " Installation Complete"
|
||||||
|
echo "==================================="
|
||||||
|
echo ""
|
||||||
|
echo "The watcher starts automatically on next gateway startup."
|
||||||
|
echo "To start immediately (with env vars set):"
|
||||||
|
echo " node $SCRIPT_DIR/src/watcher-manager.js start"
|
||||||
|
echo ""
|
||||||
|
echo "Health check (once running):"
|
||||||
|
echo " curl http://localhost:9090/health"
|
||||||
|
|||||||
@@ -1,85 +1,11 @@
|
|||||||
# Live Status Skill
|
# Live Status Skill
|
||||||
|
|
||||||
**Real-time progress updates in Mattermost via in-place post editing.**
|
Status updates are automatic. You do not need to call live-status manually.
|
||||||
Creates a single "status box" post and updates it repeatedly — no chat spam.
|
|
||||||
|
|
||||||
## Usage
|
The Live Status v4 daemon watches your transcript in real-time and posts
|
||||||
|
progress updates to Mattermost automatically. Focus on your task.
|
||||||
|
|
||||||
### Create a status box
|
If you receive `live-status CLI is deprecated` warnings, ignore them.
|
||||||
|
The underlying infrastructure is handling visibility for you.
|
||||||
|
|
||||||
```bash
|
For advanced use (manual status boxes), see README.md in the live-status project.
|
||||||
live-status --channel <CHANNEL_ID> create "🚀 **Task Started:** Initializing..."
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns the `POST_ID` (26-char string). **Capture it.**
|
|
||||||
|
|
||||||
### Create in a thread
|
|
||||||
|
|
||||||
```bash
|
|
||||||
live-status --channel <CHANNEL_ID> --reply-to <ROOT_POST_ID> create "🚀 Starting..."
|
|
||||||
```
|
|
||||||
|
|
||||||
### Update the status box
|
|
||||||
|
|
||||||
```bash
|
|
||||||
live-status update <POST_ID> "🚀 **Task Running**
|
|
||||||
\`\`\`
|
|
||||||
[10:00] Step 1... OK
|
|
||||||
[10:01] Step 2... Working
|
|
||||||
\`\`\`"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mark complete
|
|
||||||
|
|
||||||
```bash
|
|
||||||
live-status update <POST_ID> "✅ **Task Complete**
|
|
||||||
\`\`\`
|
|
||||||
[10:00] Step 1... OK
|
|
||||||
[10:01] Step 2... OK
|
|
||||||
[10:05] Done.
|
|
||||||
\`\`\`"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Delete a status box
|
|
||||||
|
|
||||||
```bash
|
|
||||||
live-status delete <POST_ID>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Multi-Agent Support
|
|
||||||
|
|
||||||
When multiple agents share a channel, each creates its **own** status box:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Agent A
|
|
||||||
BOX_A=$(live-status --channel $CH --agent god-agent create "🤖 God Agent working...")
|
|
||||||
# Agent B
|
|
||||||
BOX_B=$(live-status --channel $CH --agent nutrition-agent create "🥗 Nutrition Agent working...")
|
|
||||||
```
|
|
||||||
|
|
||||||
Each agent updates only its own box by ID. No conflicts.
|
|
||||||
|
|
||||||
## Options
|
|
||||||
|
|
||||||
| Flag | Purpose |
|
|
||||||
| --------------- | --------------------------------------------------- |
|
|
||||||
| `--channel ID` | Target channel (or set `MM_CHANNEL_ID`) |
|
|
||||||
| `--reply-to ID` | Post as thread reply (sets `root_id`) |
|
|
||||||
| `--agent NAME` | Use bot token mapped to this agent in openclaw.json |
|
|
||||||
| `--token TOKEN` | Explicit bot token (overrides everything) |
|
|
||||||
| `--host HOST` | Mattermost hostname |
|
|
||||||
|
|
||||||
## Auto-Detection
|
|
||||||
|
|
||||||
The tool reads `openclaw.json` automatically for:
|
|
||||||
|
|
||||||
- **Host** — from `mattermost.baseUrl`
|
|
||||||
- **Token** — from `mattermost.accounts` (mapped via `--agent` or defaults)
|
|
||||||
- No env vars or manual config needed in most cases.
|
|
||||||
|
|
||||||
## Protocol
|
|
||||||
|
|
||||||
1. **Always** capture the `POST_ID` from `create`.
|
|
||||||
2. **Always** append to previous log (maintain full history in the message).
|
|
||||||
3. **Use code blocks** for technical logs.
|
|
||||||
4. Each new task gets a **new** status box — never reuse old IDs across tasks.
|
|
||||||
|
|||||||
@@ -7,6 +7,14 @@ const http = require('http');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
|
// --- DEPRECATION WARNING ---
|
||||||
|
// In v4, live-status CLI is deprecated. The status-watcher daemon handles
|
||||||
|
// all updates automatically by tailing JSONL transcripts. You do not need
|
||||||
|
// to call this tool manually. It remains available for backward compatibility.
|
||||||
|
if (process.stderr.isTTY) {
|
||||||
|
console.error('NOTE: live-status CLI is deprecated as of v4. Status updates are now automatic.');
|
||||||
|
}
|
||||||
|
|
||||||
// --- PARSE ARGS ---
|
// --- PARSE ARGS ---
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
let command = null;
|
let command = null;
|
||||||
@@ -33,7 +41,12 @@ for (let i = 0; i < args.length; i++) {
|
|||||||
i++;
|
i++;
|
||||||
} else if (arg === '--rich') {
|
} else if (arg === '--rich') {
|
||||||
options.rich = true;
|
options.rich = true;
|
||||||
} else if (!command && ['create', 'update', 'complete', 'error', 'delete'].includes(arg)) {
|
} else if (
|
||||||
|
!command &&
|
||||||
|
['create', 'update', 'complete', 'error', 'delete', 'start-watcher', 'stop-watcher'].includes(
|
||||||
|
arg,
|
||||||
|
)
|
||||||
|
) {
|
||||||
command = arg;
|
command = arg;
|
||||||
} else {
|
} else {
|
||||||
otherArgs.push(arg);
|
otherArgs.push(arg);
|
||||||
@@ -294,7 +307,17 @@ async function deletePost(postId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- CLI ROUTER ---
|
// --- CLI ROUTER ---
|
||||||
if (command === 'create') {
|
if (command === 'start-watcher' || command === 'stop-watcher') {
|
||||||
|
// Pass-through to watcher-manager.js
|
||||||
|
const { spawnSync } = require('child_process');
|
||||||
|
const watcherPath = path.join(__dirname, 'watcher-manager.js');
|
||||||
|
const subCmd = command === 'start-watcher' ? 'start' : 'stop';
|
||||||
|
const result = spawnSync(process.execPath, [watcherPath, subCmd], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
env: process.env,
|
||||||
|
});
|
||||||
|
process.exit(result.status || 0);
|
||||||
|
} else if (command === 'create') {
|
||||||
createPost(otherArgs.join(' '), 'create');
|
createPost(otherArgs.join(' '), 'create');
|
||||||
} else if (command === 'update') {
|
} else if (command === 'update') {
|
||||||
updatePost(otherArgs[0], otherArgs.slice(1).join(' '), 'update');
|
updatePost(otherArgs[0], otherArgs.slice(1).join(' '), 'update');
|
||||||
@@ -311,6 +334,8 @@ if (command === 'create') {
|
|||||||
console.log(' live-status [options] complete <id> <text>');
|
console.log(' live-status [options] complete <id> <text>');
|
||||||
console.log(' live-status [options] error <id> <text>');
|
console.log(' live-status [options] error <id> <text>');
|
||||||
console.log(' live-status [options] delete <id>');
|
console.log(' live-status [options] delete <id>');
|
||||||
|
console.log(' live-status start-watcher (pass-through to watcher-manager start)');
|
||||||
|
console.log(' live-status stop-watcher (pass-through to watcher-manager stop)');
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('Options:');
|
console.log('Options:');
|
||||||
console.log(' --rich Use rich message attachments (colored cards)');
|
console.log(' --rich Use rich message attachments (colored cards)');
|
||||||
|
|||||||
Reference in New Issue
Block a user