mirror of
https://github.com/soconnor0919/hristudio.git
synced 2026-02-04 23:46:32 -05:00
feat: Implement dynamic plugin definition loading from remote/local sources and standardize action IDs using plugin metadata.
This commit is contained in:
@@ -3,32 +3,52 @@ import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { sql } from "drizzle-orm";
|
||||
import postgres from "postgres";
|
||||
import * as schema from "../src/server/db/schema";
|
||||
import { createHash } from "crypto";
|
||||
|
||||
// Database connection
|
||||
const connectionString = process.env.DATABASE_URL!;
|
||||
const connection = postgres(connectionString);
|
||||
const db = drizzle(connection, { schema });
|
||||
|
||||
// --- NAO6 Plugin Definitions (Synced from seed-nao6-plugin.ts) ---
|
||||
const NAO_PLUGIN_DEF = {
|
||||
name: "NAO6 Robot (Enhanced ROS2 Integration)",
|
||||
version: "2.0.0",
|
||||
description: "Comprehensive NAO6 robot integration for HRIStudio experiments via ROS2.",
|
||||
actions: [
|
||||
{ id: "nao_speak", name: "Speak Text", category: "speech", parametersSchema: { type: "object", properties: { text: { type: "string" }, volume: { type: "number", default: 0.7 } }, required: ["text"] } },
|
||||
{ id: "nao_gesture", name: "Perform Gesture", category: "interaction", parametersSchema: { type: "object", properties: { gesture: { type: "string", enum: ["wave", "bow", "point"] }, speed: { type: "number", default: 0.8 } } } },
|
||||
{ id: "nao_look_at", name: "Look At", category: "movement", parametersSchema: { type: "object", properties: { target: { type: "string", enum: ["participant", "screen", "away"] }, duration: { type: "number", default: 2.0 } } } },
|
||||
{ id: "nao_nod", name: "Nod Head", category: "interaction", parametersSchema: { type: "object", properties: { speed: { type: "number", default: 1.0 } } } },
|
||||
{ id: "nao_shake_head", name: "Shake Head", category: "interaction", parametersSchema: { type: "object", properties: { speed: { type: "number", default: 1.0 } } } },
|
||||
{ id: "nao_bow", name: "Bow", category: "interaction", parametersSchema: { type: "object", properties: {} } },
|
||||
{ id: "nao_open_hand", name: "Present (Open Hand)", category: "interaction", parametersSchema: { type: "object", properties: { hand: { type: "string", enum: ["left", "right", "both"], default: "right" } } } }
|
||||
]
|
||||
};
|
||||
// --- NAO6 Plugin Definitions ---
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
// Function to load plugin definition (Remote -> Local Fallback)
|
||||
async function loadNaoPluginDef() {
|
||||
const REMOTE_URL = "https://repo.hristudio.com/plugins/nao6-ros2.json";
|
||||
const LOCAL_PATH = path.join(__dirname, "../robot-plugins/plugins/nao6-ros2.json");
|
||||
|
||||
try {
|
||||
console.log(`🌐 Attempting to fetch plugin definition from ${REMOTE_URL}...`);
|
||||
const response = await fetch(REMOTE_URL, { signal: AbortSignal.timeout(3000) }); // 3s timeout
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const data = await response.json();
|
||||
console.log("✅ Successfully fetched plugin definition from remote.");
|
||||
return data;
|
||||
} catch (err) {
|
||||
console.warn(`⚠️ Remote fetch failed (${err instanceof Error ? err.message : String(err)}). Falling back to local file.`);
|
||||
const rawPlugin = fs.readFileSync(LOCAL_PATH, "utf-8");
|
||||
return JSON.parse(rawPlugin);
|
||||
}
|
||||
}
|
||||
|
||||
// Global variable to hold the loaded definition
|
||||
let NAO_PLUGIN_DEF: any;
|
||||
|
||||
async function main() {
|
||||
console.log("🌱 Starting realistic seed script...");
|
||||
|
||||
|
||||
|
||||
try {
|
||||
NAO_PLUGIN_DEF = await loadNaoPluginDef();
|
||||
|
||||
// Ensure legacy 'actions' property maps to 'actionDefinitions' if needed, though schema supports both if we map it
|
||||
if (NAO_PLUGIN_DEF.actions && !NAO_PLUGIN_DEF.actionDefinitions) {
|
||||
NAO_PLUGIN_DEF.actionDefinitions = NAO_PLUGIN_DEF.actions;
|
||||
}
|
||||
|
||||
// 1. Clean existing data (Full Wipe)
|
||||
console.log("🧹 Cleaning existing data...");
|
||||
await db.delete(schema.mediaCaptures).where(sql`1=1`);
|
||||
@@ -51,12 +71,14 @@ async function main() {
|
||||
console.log("👥 Creating users...");
|
||||
const hashedPassword = await bcrypt.hash("password123", 12);
|
||||
|
||||
const gravatarUrl = (email: string) => `https://www.gravatar.com/avatar/${createHash("md5").update(email.toLowerCase().trim()).digest("hex")}?d=identicon`;
|
||||
|
||||
const [adminUser] = await db.insert(schema.users).values({
|
||||
name: "Sean O'Connor",
|
||||
email: "sean@soconnor.dev",
|
||||
password: hashedPassword,
|
||||
emailVerified: new Date(),
|
||||
image: "https://api.dicebear.com/7.x/avataaars/svg?seed=Sean",
|
||||
image: gravatarUrl("sean@soconnor.dev"),
|
||||
}).returning();
|
||||
|
||||
const [researcherUser] = await db.insert(schema.users).values({
|
||||
@@ -98,12 +120,12 @@ async function main() {
|
||||
version: NAO_PLUGIN_DEF.version,
|
||||
description: NAO_PLUGIN_DEF.description,
|
||||
author: "HRIStudio Team",
|
||||
trustLevel: "official",
|
||||
repositoryUrl: "https://github.com/hristudio/plugins/tree/main/nao6",
|
||||
trustLevel: "verified",
|
||||
actionDefinitions: NAO_PLUGIN_DEF.actionDefinitions,
|
||||
metadata: NAO_PLUGIN_DEF,
|
||||
status: "active",
|
||||
repositoryUrl: naoRepo!.url,
|
||||
actionDefinitions: NAO_PLUGIN_DEF.actions,
|
||||
configurationSchema: { type: "object", properties: { robotIp: { type: "string", default: "192.168.1.100" } } },
|
||||
metadata: { category: "robot_control" }
|
||||
createdAt: new Date(),
|
||||
}).returning();
|
||||
|
||||
// 4. Create Study & Experiment - Comparative WoZ Study
|
||||
@@ -157,21 +179,29 @@ async function main() {
|
||||
{
|
||||
stepId: step1!.id,
|
||||
name: "Greet Participant",
|
||||
type: "nao6.nao_speak",
|
||||
type: "nao6-ros2.say_with_emotion",
|
||||
orderIndex: 0,
|
||||
parameters: { text: "Hello there! I have a wonderful story to share with you today.", volume: 0.8 },
|
||||
parameters: { text: "Hello there! I have a wonderful story to share with you today.", emotion: "happy", speed: 1.0 },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "speech",
|
||||
category: "interaction",
|
||||
retryable: true
|
||||
},
|
||||
{
|
||||
stepId: step1!.id,
|
||||
name: "Wave Greeting",
|
||||
type: "nao6.nao_gesture",
|
||||
type: "nao6-ros2.move_arm",
|
||||
orderIndex: 1,
|
||||
parameters: { gesture: "wave" },
|
||||
// Raising right arm to wave position
|
||||
parameters: {
|
||||
arm: "right",
|
||||
shoulder_pitch: -1.0,
|
||||
shoulder_roll: -0.3,
|
||||
elbow_yaw: 1.5,
|
||||
elbow_roll: 0.5,
|
||||
speed: 0.5
|
||||
},
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "interaction",
|
||||
category: "movement",
|
||||
retryable: true
|
||||
}
|
||||
]);
|
||||
@@ -191,20 +221,20 @@ async function main() {
|
||||
{
|
||||
stepId: step2!.id,
|
||||
name: "Tell Story Part 1",
|
||||
type: "nao6.nao_speak",
|
||||
type: "nao6-ros2.say_text",
|
||||
orderIndex: 0,
|
||||
parameters: { text: "Once upon a time, in a land far away, there lived a curious robot named Alpha.", volume: 0.8 },
|
||||
parameters: { text: "Once upon a time, in a land far away, there lived a curious robot named Alpha." },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "speech"
|
||||
category: "interaction"
|
||||
},
|
||||
{
|
||||
stepId: step2!.id,
|
||||
name: "Present Gesture",
|
||||
type: "nao6.nao_open_hand",
|
||||
name: "Look at Audience",
|
||||
type: "nao6-ros2.move_head",
|
||||
orderIndex: 1,
|
||||
parameters: { hand: "right" },
|
||||
parameters: { yaw: 0.0, pitch: -0.2, speed: 0.5 },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "interaction"
|
||||
category: "movement"
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -225,18 +255,22 @@ async function main() {
|
||||
{
|
||||
stepId: step3!.id,
|
||||
name: "Ask Question",
|
||||
type: "nao6.nao_speak",
|
||||
type: "nao6-ros2.say_with_emotion",
|
||||
orderIndex: 0,
|
||||
parameters: { text: "What was the robot's name?", volume: 0.8 },
|
||||
parameters: { text: "Did you understand the story so far?", emotion: "happy", speed: 1.0 },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "speech"
|
||||
category: "interaction"
|
||||
},
|
||||
{
|
||||
stepId: step3!.id,
|
||||
name: "Wait for Wizard Input",
|
||||
type: "core.wait_for_response",
|
||||
type: "wizard_wait_for_response",
|
||||
orderIndex: 1,
|
||||
parameters: { prompt: "Did participant answer 'Alpha'?", options: ["Yes", "No"] },
|
||||
parameters: {
|
||||
prompt_text: "Did participant answer 'Alpha'?",
|
||||
response_type: "verbal",
|
||||
timeout: 60
|
||||
},
|
||||
sourceKind: "core",
|
||||
category: "wizard"
|
||||
}
|
||||
@@ -257,21 +291,21 @@ async function main() {
|
||||
await db.insert(schema.actions).values([
|
||||
{
|
||||
stepId: step4!.id,
|
||||
name: "Nod Affirmation",
|
||||
type: "nao6.nao_nod",
|
||||
name: "Express Agreement",
|
||||
type: "nao6-ros2.say_with_emotion",
|
||||
orderIndex: 0,
|
||||
parameters: { speed: 1.0 },
|
||||
parameters: { text: "Yes, exactly!", emotion: "happy", speed: 1.0 },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "interaction"
|
||||
},
|
||||
{
|
||||
stepId: step4!.id,
|
||||
name: "Say Correct",
|
||||
type: "nao6.nao_speak",
|
||||
type: "nao6-ros2.say_text",
|
||||
orderIndex: 1,
|
||||
parameters: { text: "That is correct! Well done.", volume: 0.8 },
|
||||
parameters: { text: "That is correct! Well done." },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "speech"
|
||||
category: "interaction"
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -290,18 +324,18 @@ async function main() {
|
||||
{
|
||||
stepId: step5!.id,
|
||||
name: "Finish Story",
|
||||
type: "nao6.nao_speak",
|
||||
type: "nao6-ros2.say_text",
|
||||
orderIndex: 0,
|
||||
parameters: { text: "Alpha explored the world and learned many things. The end.", volume: 0.8 },
|
||||
parameters: { text: "Alpha explored the world and learned many things. The end." },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "speech"
|
||||
category: "interaction"
|
||||
},
|
||||
{
|
||||
stepId: step5!.id,
|
||||
name: "Bow Goodbye",
|
||||
type: "nao6.nao_bow",
|
||||
name: "Say Goodbye",
|
||||
type: "nao6-ros2.say_with_emotion",
|
||||
orderIndex: 1,
|
||||
parameters: {},
|
||||
parameters: { text: "Goodbye everyone!", emotion: "happy", speed: 1.0 },
|
||||
pluginId: naoPlugin!.id,
|
||||
category: "interaction"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user