OpenClaw's Codex tool dispatcher (thread-lifecycle:255) expects every
tool execute() to return { content: [...] } and calls result.content.reduce()
to compute total text length. All 9 harborforge_* tools returned bare
objects ({ running, processing, currentSlot, ... }) which has no
.content field — so .reduce of undefined threw, and the agent saw the
cryptic 'Cannot read properties of undefined (reading reduce)' on
every call. This silently blocked every calendar slot transition on
prod for hours: agents could call harborforge_calendar_complete but
it always errored, so slots never moved out of not_started.
Fix is at the registerTool boundary: api.registerTool is wrapped once
to coerce every tool's execute return through ensureMcpContentShape.
Tools that already return the correct shape are unchanged. No per-tool
edits needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
HarborForge OpenClaw Plugin
OpenClaw plugin that exposes OpenClaw-side metadata to the HarborForge Monitor, provides an optional local Monitor bridge, drives the HarborForge Calendar scheduler, and can optionally install the hf CLI.
Part of the HarborForge platform.
- Role: OpenClaw integration layer for HarborForge (registered plugin id:
harbor-forge). - Talks to the HarborForge backend (
backendUrl, defaulthttps://monitor.hangman-lab.top) for Calendar APIs. - Talks to a local HarborForge.Monitor bridge over
127.0.0.1:<monitor_port>(no fixed default; commonly9100).
Current State
- Plugin registration id:
harbor-forge(washarborforge-monitor) - Plugin version:
0.2.0(package manifests); telemetry reportspluginVersion0.3.1 - Legacy sidecar
server/architecture removed — telemetry is served directly by the plugin - Monitor bridge runs over the local
monitor_port - Calendar scheduler integration (PLG-CAL-001 / 002 / 004)
- Installer supports
--install-cliand an optional managed Monitor (--install-monitor) skills/hf/is installed only with--install-cli
Project Structure
HarborForge.OpenclawPlugin/
├── package.json
├── README.md
├── docs/ # design notes (calendar, monitor connector)
├── plugin/
│ ├── openclaw.plugin.json # plugin manifest + config schema
│ ├── index.ts # plugin entry, tool registration
│ ├── tsconfig.json
│ ├── core/
│ │ ├── config.ts # config defaults / resolution
│ │ ├── managed-monitor.ts # optionally spawn HarborForge.Monitor
│ │ ├── monitor-bridge.ts # client for the Monitor bridge
│ │ └── openclaw-agents.ts # enumerate OpenClaw agents
│ ├── calendar/ # Calendar scheduler + bridge
│ │ ├── index.ts
│ │ ├── scheduler.ts
│ │ ├── calendar-bridge.ts
│ │ └── types.ts
│ ├── hooks/
│ │ ├── gateway-start.ts
│ │ └── gateway-stop.ts
│ └── package.json
├── skills/
│ └── hf/
│ └── SKILL.md # installed only with --install-cli
└── scripts/
└── install.mjs
Installation
Standard Install
node scripts/install.mjs
This will:
- Build and install the OpenClaw plugin
- Copy regular skills
- Not install the
hfbinary - Not copy
skills/hf/
Plugin + hf CLI
node scripts/install.mjs --install-cli
This additionally:
- Builds
HarborForge.Cli(go build ./cmd/hf) - Installs
hfto<openclaw>/bin/hf(default~/.openclaw/bin/hf) andchmod +x - Copies
skills/hf/into the OpenClaw skills directory
Common Options
# Build only (no install / config)
node scripts/install.mjs --build-only
# Install only, skip dependency checks
node scripts/install.mjs --skip-check
# Specify OpenClaw profile path (also honors OPENCLAW_PATH env)
node scripts/install.mjs --openclaw-profile-path /custom/path/.openclaw
# Build and install a managed HarborForge.Monitor binary alongside the plugin
node scripts/install.mjs --install-monitor yes --monitor-branch main
# Verbose logs
node scripts/install.mjs --verbose
# Uninstall (plugin, config entries, hf binary, managed monitor)
node scripts/install.mjs --uninstall
The installer also updates OpenClaw config (plugins.load.paths, plugins.allow, plugins.entries.harbor-forge.enabled) via openclaw config.
Configuration
Edit ~/.openclaw/openclaw.json:
{
"plugins": {
"entries": {
"harbor-forge": {
"enabled": true,
"config": {
"enabled": true,
"backendUrl": "https://monitor.hangman-lab.top",
"identifier": "my-server-01",
"apiKey": "your-api-key-here",
"monitor_port": 9100,
"reportIntervalSec": 30,
"httpFallbackIntervalSec": 60,
"logLevel": "info",
"calendarEnabled": true,
"calendarHeartbeatIntervalSec": 60
}
}
}
}
}
Then restart:
openclaw gateway restart
Config Options
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Enable the plugin |
backendUrl |
string | https://monitor.hangman-lab.top |
HarborForge backend base URL (Monitor + Calendar APIs) |
identifier |
string | hostname | Server / claw identifier |
apiKey |
string | (none) | Server API key from the HarborForge Monitor admin panel |
monitor_port |
number | (none) | Local bridge port; plugin talks to HarborForge.Monitor via 127.0.0.1:<monitor_port> |
reportIntervalSec |
number | 30 |
Metadata push interval (seconds) |
httpFallbackIntervalSec |
number | 60 |
HTTP heartbeat interval when WS unavailable |
logLevel |
string | info |
Log level: debug / info / warn / error |
calendarEnabled |
boolean | true |
Enable Calendar scheduler integration (PLG-CAL-001) |
calendarHeartbeatIntervalSec |
number | 60 |
Calendar heartbeat interval (seconds) |
calendarApiKey |
string | (none) | API key for Calendar API auth; falls back to apiKey / X-Agent-ID |
managedMonitor |
string | (none) | Absolute path to a HarborForge.Monitor binary; if set, gateway start/stop hooks spawn/stop it |
Local Monitor Bridge
When the plugin has monitor_port configured and HarborForge.Monitor uses the same MONITOR_PORT:
- Monitor serves a local bridge on
127.0.0.1:<MONITOR_PORT> - The plugin probes
GET /health - The plugin tool
harborforge_monitor_telemetryreadsGET /telemetry - The plugin pushes OpenClaw metadata (version, plugin version, agents) via
POST /openclawon thereportIntervalSeccadence, enriching Monitor heartbeats - If the bridge port is unconfigured or unreachable, the plugin still works normally
This link is an optional enhancement, not a precondition for the plugin to start or for Monitor heartbeats.
Managed Monitor
If managedMonitor points to an installed HarborForge.Monitor binary, the gateway_start hook spawns it (passing --backend-url, --identifier, --api-key, --monitor-port, --report-interval, --log-level from the plugin config) and gateway_stop terminates it. Use install.mjs --install-monitor yes to build and wire this automatically.
Calendar Scheduler
When calendarEnabled is true, on gateway start the plugin starts a Calendar scheduler that heartbeats the backend (/calendar/agent/heartbeat, /calendar/agent/status, /calendar/agent/notify) to receive and run scheduled TimeSlots, waking/spawning agents via the OpenClaw spawn API (with a notification fallback). Scheduler state is persisted to a state file; gateway restarts can be requested by the backend (PLG-CAL-004).
Tools Provided
| Tool | Description |
|---|---|
harborforge_status |
Plugin status, resolved config, Monitor bridge health, calendar status, telemetry snapshot |
harborforge_telemetry |
Current system telemetry snapshot from this host |
harborforge_monitor_telemetry |
Query the Monitor bridge for host hardware telemetry |
harborforge_calendar_status |
Calendar scheduler status and current slot |
harborforge_calendar_complete |
Complete the current calendar slot with actual duration |
harborforge_calendar_abort |
Abort the current calendar slot |
harborforge_calendar_pause |
Pause the current calendar slot |
harborforge_calendar_resume |
Resume the paused calendar slot |
harborforge_restart_status |
Check whether a gateway restart is pending |
Telemetry Snapshot Fields
identifier,hostname,platform,timestampuptimememory:total/free/used/usagePercentload:avg1/avg5/avg15openclaw:version/pluginVersion
Development
cd plugin
npm install
npm run build
The build runs tsc and emits dist/ (dist/index.js is the plugin entry).
Dependencies
- Node.js 18+
- OpenClaw Gateway
- Go 1.20+ (only for
--install-cli/--install-monitor)
Tips
- After installing
hf, add~/.openclaw/binto yourPATH - When an agent uses
hf, tryhf --help-brieffirst - For the full command tree, see
hf --help