Files
openclaw/scripts/test-matrix-client.mjs
Tabula Myriad TM-1 536bba8912 feat: complete Matrix JS SDK client for triad inter-node sync
- Replace stub lib/triad-matrix-client.js with full matrix-js-sdk implementation
- Add TriadMatrixClient class with connect, startSync, sendMessage, getMessages, joinRoom, getOrCreateRoom, inviteUser
- Add test-matrix-client.mjs test script
- Add tabula-backup skill for hourly node state backup to Tabula_Myriad repo
- Update MEMORY.md with corrected triad state

The Matrix client enables:
- Node auth as users (not bots)
- Consensus room #consensus:silica-animus.local
- Direct TM1<->TM2<->TM3 communication via Dendrite homeserver

TM-1 authority commit
2026-03-24 15:11:21 -04:00

189 lines
6.3 KiB
JavaScript

#!/usr/bin/env node
/**
* @file test-matrix-client.mjs
* @description Test script for TriadMatrixClient
*
* Tests:
* 1. Connect to Matrix homeserver as @tm1:silica-animus.local
* 2. Join or create the consensus room #consensus:silica-animus.local
* 3. Send a test message "TM-1 matrix client test"
* 4. Verify the message appears in the room
*
* Usage: node scripts/test-matrix-client.mjs
*/
import { readFileSync, existsSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
const __dirname = dirname(fileURLToPath(import.meta.url));
// Configuration
const CONFIG = {
homeserver: process.env.MATRIX_HOMESERVER || "http://127.0.0.1:8008",
userId: process.env.MATRIX_USER_ID || "@tm1:silica-animus.local",
consensusRoomAlias: process.env.MATRIX_CONSENSUS_ROOM || "#consensus:silica-animus.local",
testMessage: "TM-1 matrix client test",
tokenPath: join(__dirname, "..", ".secure", "matrix", "tm1.token"),
};
console.log("=".repeat(60));
console.log("TriadMatrixClient Test Script");
console.log("=".repeat(60));
console.log("Config:");
console.log(` homeserver: ${CONFIG.homeserver}`);
console.log(` userId: ${CONFIG.userId}`);
console.log(` consensusRoom: ${CONFIG.consensusRoomAlias}`);
console.log(` testMessage: ${CONFIG.testMessage}`);
console.log("=".repeat(60));
// Import the client
const { TriadMatrixClient } = await import("../lib/triad-matrix-client.js");
/**
* Load access token from .secure/matrix/tm1.token
*/
function loadToken() {
const tokenFile = CONFIG.tokenPath;
console.log(`\n[1] Loading token from ${tokenFile}`);
if (!existsSync(tokenFile)) {
throw new Error(
`Token file not found: ${tokenFile}. Run the login flow first to obtain a token.`,
);
}
const token = readFileSync(tokenFile, "utf8").trim();
if (!token) {
throw new Error("Token file is empty");
}
console.log(` Token loaded (${token.length} chars)`);
return token;
}
/**
* Test the Matrix client
*/
async function runTests() {
const client = new TriadMatrixClient();
let token = null;
try {
// Step 1: Load token and connect
console.log("\n[2] Connecting to Matrix homeserver...");
token = loadToken();
await client.connect(CONFIG.homeserver, CONFIG.userId, token);
console.log(` Connected: ${client.connected}`);
if (!client.connected) {
throw new Error("Failed to connect");
}
// Step 2: Start sync to receive updates
console.log("\n[3] Starting sync...");
await client.startSync({
onMessage: (roomId, event) => {
console.log(` [INCOMING] ${event.sender}: ${event.content?.body}`);
},
onInvite: (roomId, inviter) => {
console.log(` [INVITE] ${inviter} invited you to ${roomId}`);
},
});
console.log(" Sync started");
// Step 3: Get or create the consensus room
console.log("\n[4] Getting/creating consensus room...");
const consensusRoomId = await client.getOrCreateRoom(CONFIG.consensusRoomAlias, {
name: "Triad Consensus Room",
topic: "Official triad node consensus coordination — 2-of-3 quorum required",
});
console.log(` Consensus room ID: ${consensusRoomId}`);
// Step 4: Join the room (if not already joined)
console.log("\n[5] Joining consensus room...");
try {
const joinedRoomId = await client.joinRoom(CONFIG.consensusRoomAlias);
console.log(` Joined room: ${joinedRoomId}`);
} catch (err) {
console.log(` Join result: ${err.message} (may already be joined)`);
}
// Step 5: Get current messages before sending
console.log("\n[6] Getting current messages in room...");
const beforeMessages = await client.getMessages(CONFIG.consensusRoomAlias, 5);
console.log(` Current message count: ${beforeMessages.length}`);
beforeMessages.forEach((msg, i) => {
console.log(
` [${i + 1}] ${msg.sender}: ${msg.body} (${new Date(msg.timestamp).toISOString()})`,
);
});
// Step 6: Send the test message
console.log("\n[7] Sending test message...");
console.log(` Message: "${CONFIG.testMessage}"`);
const sendResult = await client.sendMessage(CONFIG.consensusRoomAlias, CONFIG.testMessage);
console.log(` Send result: eventId=${sendResult.eventId}, timestamp=${sendResult.timestamp}`);
if (!sendResult.eventId) {
throw new Error("Send did not return an eventId");
}
// Step 7: Wait a moment for sync to process the message
console.log("\n[8] Waiting for sync to process...");
await new Promise((resolve) => setTimeout(resolve, 2000));
// Step 8: Verify the message appears in the room
console.log("\n[9] Verifying message was sent...");
const afterMessages = await client.getMessages(CONFIG.consensusRoomAlias, 5);
console.log(` Message count after send: ${afterMessages.length}`);
const ourMessage = afterMessages.find(
(msg) => msg.body === CONFIG.testMessage && msg.sender === CONFIG.userId,
);
if (ourMessage) {
console.log(` ✓ Message verified!`);
console.log(` eventId: ${ourMessage.eventId}`);
console.log(` sender: ${ourMessage.sender}`);
console.log(` body: ${ourMessage.body}`);
console.log(` timestamp: ${new Date(ourMessage.timestamp).toISOString()}`);
} else {
console.log(` ✗ Message not found in recent messages`);
console.log(` Recent messages:`);
afterMessages.forEach((msg, i) => {
console.log(` [${i + 1}] ${msg.sender}: ${msg.body}`);
});
}
// Step 9: List all joined rooms
console.log("\n[10] Listing joined rooms...");
const rooms = client.getJoinedRooms();
console.log(` Joined rooms: ${rooms.length}`);
rooms.forEach((room) => {
console.log(` - ${room.name} (${room.roomId}) - ${room.memberCount} members`);
});
console.log("\n" + "=".repeat(60));
console.log("TEST RESULT: SUCCESS");
console.log("=".repeat(60));
} catch (err) {
console.error("\n" + "=".repeat(60));
console.error("TEST RESULT: FAILED");
console.error(`Error: ${err.message}`);
console.error("=".repeat(60));
process.exitCode = 1;
} finally {
// Cleanup
console.log("\n[11] Disconnecting...");
await client.disconnect();
console.log(" Disconnected");
}
}
// Run the tests
runTests().catch((err) => {
console.error("Unhandled error:", err);
process.exit(1);
});