mother: drop duplicate scripts from iso (canonical = colibri) + docs → hive_nodes #129
6 changed files with 8 additions and 148 deletions
|
|
@ -44,7 +44,7 @@ bill-of-materials calculation. The obvious answer was Blender — it has
|
|||
|
||||
## Registering capabilities
|
||||
|
||||
This comparison feeds into the mother node's PostgreSQL `usb_nodes` table.
|
||||
This comparison feeds into the mother node's PostgreSQL `hive_nodes` table.
|
||||
When a node registers its hardware profile, the capability derivation trigger
|
||||
marks nodes that can run `geodesic-dome-mcp` (all of them — numpy + Pillow
|
||||
are universal). Nodes that have Blender installed get an additional
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
Connects a booted Clawdie USB to the mother node (OSA) via MCP over SSH.
|
||||
After setup, `clawdie-hw-probe` runs on the USB, the hardware profile is
|
||||
sent to mother, and stored in PostgreSQL `mother_hive.usb_nodes`.
|
||||
sent to mother, and stored in PostgreSQL `mother_hive.hive_nodes`.
|
||||
|
||||
## Hosts used in this guide
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ sent to mother, and stored in PostgreSQL `mother_hive.usb_nodes`.
|
|||
│ colibri-mcp-ssh → strips forced-command wrapper │
|
||||
│ → passes "tools" subcommand to colibri-mcp │
|
||||
│ │
|
||||
│ PostgreSQL mother_hive.usb_nodes ← hw-probe JSON stored │
|
||||
│ PostgreSQL mother_hive.hive_nodes ← hw-probe JSON stored │
|
||||
│ │
|
||||
└───────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
|
@ -200,7 +200,7 @@ HW_JSON=$(sudo clawdie-hw-probe 2>/dev/null)
|
|||
# 2. View what would be sent to mother
|
||||
echo "$HW_JSON" | python3.11 -m json.tool | head -15
|
||||
|
||||
# 3. Send to mother via MCP (node_register tool: packaging/mother/node-register-mcp)
|
||||
# 3. Send to mother via MCP (node_register tool lives in the colibri repo: packaging/mother/node-register-mcp)
|
||||
# The 0.12 daemon collects hw-probe at autospawn time and passes it to agents
|
||||
# via CLAWDIE_HW_PROFILE env var. For manual testing, pipe JSON-RPC directly:
|
||||
printf '{"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"node_register","arguments":{"hostname":"%s","hw_profile":%s}}}\n' \
|
||||
|
|
@ -208,7 +208,7 @@ printf '{"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"node_reg
|
|||
|
||||
# 4. Verify on mother
|
||||
ssh m0th3r 'sudo -u postgres psql -d mother_hive \
|
||||
-c "SELECT hostname, status, capabilities FROM usb_nodes;"'
|
||||
-c "SELECT hostname, status, capabilities FROM hive_nodes;"'
|
||||
# Expected: both osa.smilepowered.org and clawdie-usb listed
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
Boot the live USB, and the colibri-daemon automatically connects to mother
|
||||
(OSA) via MCP over SSH. The first action on autospawn: `clawdie-hw-probe` runs,
|
||||
hardware profile is sent to mother via `colibri_external_mcp_call_tool`, and
|
||||
mother stores it in PostgreSQL `mother_hive.usb_nodes`.
|
||||
mother stores it in PostgreSQL `mother_hive.hive_nodes`.
|
||||
|
||||
## Current state
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ HW_JSON=$(sudo clawdie-hw-probe 2>/dev/null)
|
|||
# with the mother server registered)
|
||||
|
||||
# 3. Verify on mother:
|
||||
sudo -u postgres psql -d mother_hive -c "SELECT hostname, status, capabilities FROM usb_nodes;"
|
||||
sudo -u postgres psql -d mother_hive -c "SELECT hostname, status, capabilities FROM hive_nodes;"
|
||||
```
|
||||
|
||||
Expected: the USB node appears in PostgreSQL with hardware profile and
|
||||
|
|
@ -154,7 +154,7 @@ derived capabilities (`has_gpu`, `ram_gb`, `cpu_cores`, etc.).
|
|||
The agent's first action should read that env var and call
|
||||
`colibri_external_mcp_call_tool(server="mother", tool="node_register")`.
|
||||
The `node_register` MCP tool is now implemented at
|
||||
`packaging/mother/node-register-mcp`.
|
||||
the **colibri** repo (`packaging/mother/node-register-mcp`).
|
||||
|
||||
3. **Tailscale dependency**: The USB needs Tailscale to be up before MCP works.
|
||||
This is already the case — Tailscale auth is part of the bootstrap flow.
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
#!/bin/sh
|
||||
# build-colibri MCP tool — build a colibri crate from any git branch.
|
||||
#
|
||||
# Accepts a JSON-RPC tools/call request on stdin, builds the requested
|
||||
# crate, and returns the result as a JSON content block to stdout.
|
||||
#
|
||||
# Expected input (MCP tools/call):
|
||||
# {"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"build_colibri","arguments":{"crate":"colibri-daemon","branch":"main","features":"","release":"true"}}}
|
||||
#
|
||||
# Parameters:
|
||||
# crate — workspace member to build (default: colibri-daemon)
|
||||
# branch — git branch/ref to check out (default: main)
|
||||
# features — optional comma-separated features
|
||||
# release — "true" for --release, anything else for debug (default: true)
|
||||
#
|
||||
# Output: MCP JSON-RPC response with text content block
|
||||
# {"success":true,"binary":"/home/clawdie/ai/colibri/target/release/colibri-daemon","duration_s":25.4,"branch":"main","commit":"abc1234"}
|
||||
# or
|
||||
# {"success":false,"error":"build failed: ..."}
|
||||
set -eu
|
||||
|
||||
REPO="/home/clawdie/ai/colibri"
|
||||
CRATE_DEFAULT="colibri-daemon"
|
||||
BRANCH_DEFAULT="main"
|
||||
|
||||
# Read JSON from stdin, extract arguments
|
||||
INPUT=$(cat)
|
||||
CRATE=$(echo "$INPUT" | jq -r '.params.arguments.crate // "'"$CRATE_DEFAULT"'"')
|
||||
BRANCH=$(echo "$INPUT" | jq -r '.params.arguments.branch // "'"$BRANCH_DEFAULT"'"')
|
||||
FEATURES=$(echo "$INPUT" | jq -r '.params.arguments.features // ""')
|
||||
RELEASE=$(echo "$INPUT" | jq -r '.params.arguments.release // "true"')
|
||||
ID=$(echo "$INPUT" | jq -r '.id // "1"')
|
||||
|
||||
# Build flags
|
||||
CARGO_FLAGS=""
|
||||
if [ "$RELEASE" = "true" ]; then
|
||||
CARGO_FLAGS="--release"
|
||||
fi
|
||||
if [ -n "$FEATURES" ]; then
|
||||
CARGO_FLAGS="$CARGO_FLAGS --features $FEATURES"
|
||||
fi
|
||||
|
||||
START_TS=$(date +%s)
|
||||
|
||||
# Fetch and checkout
|
||||
cd "$REPO"
|
||||
git fetch origin 2>&1 || true
|
||||
if ! git checkout "$BRANCH" 2>&1; then
|
||||
printf '{"jsonrpc":"2.0","id":%s,"error":{"code":-1,"message":"git checkout failed: %s"}}\n' "$ID" "$BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
COMMIT=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||
|
||||
# Build
|
||||
BUILD_LOG=$(mktemp)
|
||||
if cargo build $CARGO_FLAGS -p "$CRATE" >"$BUILD_LOG" 2>&1; then
|
||||
DURATION=$(( $(date +%s) - START_TS ))
|
||||
BINARY=$(find target -maxdepth 3 -type f -name "$CRATE" -perm -111 | grep -v deps | head -1)
|
||||
SIZE=$(stat -f '%z' "$BINARY" 2>/dev/null || echo "0")
|
||||
cat "$BUILD_LOG" | tail -5 >&2 # send tail to stderr for logging
|
||||
printf '{"jsonrpc":"2.0","id":%s,"result":{"content":[{"type":"text","text":"{\\"success\\":true,\\"binary\\":\\"%s\\",\\"duration_s\\":%s,\\"branch\\":\\"%s\\",\\"commit\\":\\"%s\\",\\"size_bytes\\":%s}"}]}}\n' \
|
||||
"$ID" "$BINARY" "$DURATION" "$BRANCH" "$COMMIT" "$SIZE"
|
||||
else
|
||||
DURATION=$(( $(date +%s) - START_TS ))
|
||||
ERROR=$(tail -20 "$BUILD_LOG" | head -10 | tr '\n' ' ' | sed 's/"/\\"/g')
|
||||
printf '{"jsonrpc":"2.0","id":%s,"error":{"code":-1,"message":"build failed in %ss: %s"}}\n' "$ID" "$DURATION" "$ERROR"
|
||||
fi
|
||||
rm -f "$BUILD_LOG"
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Wrapper for SSH forced-command MCP entrypoint.
|
||||
# SSH's command="..." restriction replaces the client's command with
|
||||
# this script and stores the original in $SSH_ORIGINAL_COMMAND.
|
||||
# Pass it through to colibri-mcp so `ssh colibri@mother tools` works.
|
||||
exec /usr/local/bin/colibri-mcp ${SSH_ORIGINAL_COMMAND}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
#!/bin/sh
|
||||
# node-register MCP tool — register a USB node's hardware profile in PostgreSQL.
|
||||
#
|
||||
# Accepts a JSON-RPC tools/call request on stdin, inserts the hw_profile into
|
||||
# mother_hive.usb_nodes, and returns the result to stdout. The
|
||||
# derive_capabilities() trigger auto-computes has_gpu, gpu_vendor,
|
||||
# can_run_local_llm, has_wifi, etc. on INSERT.
|
||||
#
|
||||
# Expected input:
|
||||
# {"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"node_register","arguments":{"hostname":"clawdie-usb","hw_profile":{...}}}}
|
||||
#
|
||||
# Output on success:
|
||||
# {"jsonrpc":"2.0","id":1,"result":{"content":[{"type":"text","text":"{\"registered\":true,\"hostname\":\"clawdie-usb\",\"capabilities\":{...}}"}]}}
|
||||
#
|
||||
# PostgreSQL access: peer auth for the 'colibri' OS user. The operator must run
|
||||
# once on mother (as postgres):
|
||||
# CREATE ROLE colibri WITH LOGIN;
|
||||
# GRANT CONNECT ON DATABASE mother_hive TO colibri;
|
||||
# GRANT INSERT, UPDATE ON usb_nodes TO colibri;
|
||||
# GRANT USAGE ON SEQUENCE usb_nodes_id_seq TO colibri;
|
||||
set -eu
|
||||
|
||||
DB="mother_hive"
|
||||
|
||||
# Read JSON-RPC from stdin
|
||||
INPUT=$(cat)
|
||||
ID=$(echo "$INPUT" | jq -r '.id // "1"')
|
||||
HOSTNAME=$(echo "$INPUT" | jq -r '.params.arguments.hostname // ""')
|
||||
HW_PROFILE=$(echo "$INPUT" | jq -c '.params.arguments.hw_profile // {}')
|
||||
|
||||
if [ -z "$HOSTNAME" ]; then
|
||||
printf '{"jsonrpc":"2.0","id":%s,"error":{"code":-1,"message":"missing required argument: hostname"}}\n' "$ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$HW_PROFILE" = "{}" ]; then
|
||||
printf '{"jsonrpc":"2.0","id":%s,"error":{"code":-1,"message":"missing required argument: hw_profile"}}\n' "$ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Escape for psql string literal: double any single quotes, wrap in E'...'
|
||||
HW_ESCAPED=$(printf '%s' "$HW_PROFILE" | sed "s/'/''/g")
|
||||
HOST_ESCAPED=$(printf '%s' "$HOSTNAME" | sed "s/'/''/g")
|
||||
|
||||
# Insert/update. The derive_capabilities() trigger fires on INSERT and
|
||||
# UPDATE OF hw_profile, so capabilities are always fresh.
|
||||
SQL="INSERT INTO usb_nodes (hostname, hw_profile, status, last_seen)
|
||||
VALUES (E'${HOST_ESCAPED}', E'${HW_ESCAPED}'::jsonb, 'online', now())
|
||||
ON CONFLICT (hostname) DO UPDATE
|
||||
SET hw_profile = EXCLUDED.hw_profile,
|
||||
status = 'online',
|
||||
last_seen = now()"
|
||||
|
||||
RESULT=$(psql -d "$DB" -tAc "BEGIN; $SQL; SELECT json_build_object(
|
||||
'registered', true,
|
||||
'hostname', hostname,
|
||||
'capabilities', capabilities
|
||||
) FROM usb_nodes WHERE hostname = E'${HOST_ESCAPED}'; COMMIT;" 2>&1) || {
|
||||
printf '{"jsonrpc":"2.0","id":%s,"error":{"code":-1,"message":"psql failed: %s"}}\n' \
|
||||
"$ID" "$(printf '%s' "$RESULT" | sed 's/"/\\"/g' | tr '\n' ' ')"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# RESULT is the JSON object from json_build_object
|
||||
printf '{"jsonrpc":"2.0","id":%s,"result":{"content":[{"type":"text","text":"%s"}]}}\n' \
|
||||
"$ID" "$(printf '%s' "$RESULT" | sed 's/"/\\"/g')"
|
||||
Loading…
Add table
Reference in a new issue