Initial commit (Sanitized)
This commit is contained in:
commit
a0acc38fa6
26
README.md
Normal file
26
README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# OpenClaw Live Status Tool
|
||||||
|
|
||||||
|
A lightweight CLI tool for OpenClaw agents to provide "Antigravity-style" live status updates in Mattermost channels (and others) without spamming.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- **Live Updates:** Create a single message and update it repeatedly.
|
||||||
|
- **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
|
||||||
|
|
||||||
|
Run the interactive installer wizard:
|
||||||
|
```bash
|
||||||
|
./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a new status box
|
||||||
|
ID=$(live-status create "Initializing...")
|
||||||
|
|
||||||
|
# Update the status box
|
||||||
|
live-status update $ID "Working..."
|
||||||
|
```
|
||||||
69
install.sh
Executable file
69
install.sh
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# OpenClaw Live Status Installer (Wizard Mode)
|
||||||
|
|
||||||
|
echo "=========================================="
|
||||||
|
echo " OpenClaw Live Status Tool - Installer "
|
||||||
|
echo "=========================================="
|
||||||
|
|
||||||
|
# 1. Install Binary
|
||||||
|
echo "[+] Installing binary to /usr/local/bin/live-status..."
|
||||||
|
cp src/live-status.js /usr/local/bin/live-status
|
||||||
|
chmod +x /usr/local/bin/live-status
|
||||||
|
|
||||||
|
# 2. Install Skill
|
||||||
|
echo "[+] Installing skill to /app/skills/live-status..."
|
||||||
|
mkdir -p /app/skills/live-status
|
||||||
|
cp skill/SKILL.md /app/skills/live-status/SKILL.md
|
||||||
|
|
||||||
|
# 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 "[?] 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
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=========================================="
|
||||||
|
echo " Installation Complete!"
|
||||||
|
echo "=========================================="
|
||||||
|
echo "Usage: live-status create \"...\""
|
||||||
40
skill/SKILL.md
Normal file
40
skill/SKILL.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Live Status Skill
|
||||||
|
|
||||||
|
**Use this tool to report real-time progress updates to Mattermost.**
|
||||||
|
It allows you to create a "Live Log" post that you update in-place, reducing chat spam.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### 1. Initialize (Start of Task)
|
||||||
|
Create a new status post. It will print the `POST_ID`.
|
||||||
|
**Required:** Pass the `CHANNEL_ID` if known (otherwise it defaults to system channel).
|
||||||
|
```bash
|
||||||
|
live-status create <CHANNEL_ID> "🚀 **Task Started:** Initializing..."
|
||||||
|
```
|
||||||
|
**Output:** `p6...` (The Post ID)
|
||||||
|
|
||||||
|
### 2. Update (During Task)
|
||||||
|
Update the post with new log lines. Use a code block for logs.
|
||||||
|
```bash
|
||||||
|
live-status update <POST_ID> "🚀 **Task Started:** Initializing...
|
||||||
|
\`\`\`
|
||||||
|
[10:00] Checking files... OK
|
||||||
|
[10:01] Downloading assets...
|
||||||
|
\`\`\`"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Complete (End of Task)
|
||||||
|
Mark as done.
|
||||||
|
```bash
|
||||||
|
live-status update <POST_ID> "✅ **Task Complete.**
|
||||||
|
\`\`\`
|
||||||
|
[10:00] Checking files... OK
|
||||||
|
[10:01] Downloading assets... Done.
|
||||||
|
[10:05] Verifying... Success.
|
||||||
|
\`\`\`"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protocol
|
||||||
|
- **Always** capture the `POST_ID` from the `create` command.
|
||||||
|
- **Always** append to the previous log (maintain history).
|
||||||
|
- **Use Code Blocks** for technical logs.
|
||||||
110
src/live-status.js
Executable file
110
src/live-status.js
Executable file
@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
const https = require('http'); // Using http for mattermost:8065 (no ssl inside docker network)
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// --- HELPER: PARSE ARGS ---
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
let command = null;
|
||||||
|
let options = {};
|
||||||
|
let otherArgs = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
if (args[i] === '--channel') {
|
||||||
|
options.channel = args[i+1];
|
||||||
|
i++; // Skip next arg (the channel ID)
|
||||||
|
} else if (!command && (args[i] === 'create' || args[i] === 'update')) {
|
||||||
|
command = args[i];
|
||||||
|
} else {
|
||||||
|
otherArgs.push(args[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CONFIGURATION (DYNAMIC) ---
|
||||||
|
const CONFIG = {
|
||||||
|
host: 'mattermost',
|
||||||
|
port: 8065,
|
||||||
|
token: 'DEFAULT_TOKEN_PLACEHOLDER', // Set via install.sh wizard
|
||||||
|
// Priority: 1. CLI Flag, 2. Env Var, 3. Hardcoded Fallback (Project-0)
|
||||||
|
channel_id: options.channel || process.env.MM_CHANNEL_ID || process.env.CHANNEL_ID || 'obzja4hb8pd85xk45xn4p31jye'
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- HELPER: HTTP REQUEST ---
|
||||||
|
function request(method, path, data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const options = {
|
||||||
|
hostname: CONFIG.host,
|
||||||
|
port: CONFIG.port,
|
||||||
|
path: '/api/v4' + path,
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${CONFIG.token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let body = '';
|
||||||
|
res.on('data', (chunk) => body += chunk);
|
||||||
|
res.on('end', () => {
|
||||||
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||||
|
try {
|
||||||
|
resolve(JSON.parse(body));
|
||||||
|
} catch (e) {
|
||||||
|
resolve(body);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject(new Error(`Request Failed (${res.statusCode}): ${body}`));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (e) => reject(e));
|
||||||
|
if (data) req.write(JSON.stringify(data));
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- COMMANDS ---
|
||||||
|
|
||||||
|
async function createPost(text) {
|
||||||
|
try {
|
||||||
|
const result = await request('POST', '/posts', {
|
||||||
|
channel_id: CONFIG.channel_id,
|
||||||
|
message: text
|
||||||
|
});
|
||||||
|
console.log(result.id);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Error creating post in channel ${CONFIG.channel_id}:`, e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updatePost(postId, text) {
|
||||||
|
try {
|
||||||
|
const current = await request('GET', `/posts/${postId}`);
|
||||||
|
await request('PUT', `/posts/${postId}`, {
|
||||||
|
id: postId,
|
||||||
|
message: text,
|
||||||
|
props: current.props
|
||||||
|
});
|
||||||
|
console.log("updated");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error updating post:", e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CLI ROUTER ---
|
||||||
|
if (command === 'create') {
|
||||||
|
const text = otherArgs.join(' ');
|
||||||
|
createPost(text);
|
||||||
|
} else if (command === 'update') {
|
||||||
|
const id = otherArgs[0];
|
||||||
|
const text = otherArgs.slice(1).join(' ');
|
||||||
|
updatePost(id, text);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: live-status [--channel ID] create <text>");
|
||||||
|
console.log(" live-status update <id> <text>");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user