diff --git a/bun.lock b/bun.lock index 1be1bb3..2dc7761 100644 --- a/bun.lock +++ b/bun.lock @@ -49,7 +49,7 @@ "js-cookie": "^3.0.5", "lucide-react": "^0.536.0", "minio": "^8.0.6", - "next": "^16.0.10", + "next": "^16.1.6", "next-auth": "^5.0.0-beta.29", "next-themes": "^0.4.6", "postgres": "^3.4.4", @@ -70,6 +70,7 @@ "@eslint/eslintrc": "^3.3.1", "@tailwindcss/postcss": "^4.0.15", "@types/bcryptjs": "^3.0.0", + "@types/crypto-js": "^4.2.2", "@types/node": "^20.14.10", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", @@ -347,25 +348,25 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], - "@next/env": ["@next/env@16.0.10", "", {}, "sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang=="], + "@next/env": ["@next/env@16.1.6", "", {}, "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ=="], "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.4.5", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-YhbrlbEt0m4jJnXHMY/cCUDBAWgd5SaTa5mJjzOt82QwflAFfW/h3+COp2TfVSzhmscIZ5sg2WXt3MLziqCSCw=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.0.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.0.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.0.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.1.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.0.10", "", { "os": "linux", "cpu": "x64" }, "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.0.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.1.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.0.10", "", { "os": "win32", "cpu": "x64" }, "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -633,6 +634,8 @@ "@types/bcryptjs": ["@types/bcryptjs@3.0.0", "", { "dependencies": { "bcryptjs": "*" } }, "sha512-WRZOuCuaz8UcZZE4R5HXTco2goQSI2XxjGY3hbM/xDvwmqFWd4ivooImsMx65OKM6CtNKbnZ5YL+YwAwK7c1dg=="], + "@types/crypto-js": ["@types/crypto-js@4.2.2", "", {}, "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/js-cookie": ["@types/js-cookie@3.0.6", "", {}, "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ=="], @@ -759,6 +762,8 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], + "bcryptjs": ["bcryptjs@3.0.2", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-k38b3XOZKv60C4E2hVsXTolJWfkGRMbILBIe2IBITXciy5bOsTKot5kDrf3ZfufQtQOUN5mXceUEpU1rTl9Uog=="], "bl": ["bl@5.1.0", "", { "dependencies": { "buffer": "^6.0.3", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ=="], @@ -1191,7 +1196,7 @@ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], - "next": ["next@16.0.10", "", { "dependencies": { "@next/env": "16.0.10", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.0.10", "@next/swc-darwin-x64": "16.0.10", "@next/swc-linux-arm64-gnu": "16.0.10", "@next/swc-linux-arm64-musl": "16.0.10", "@next/swc-linux-x64-gnu": "16.0.10", "@next/swc-linux-x64-musl": "16.0.10", "@next/swc-win32-arm64-msvc": "16.0.10", "@next/swc-win32-x64-msvc": "16.0.10", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA=="], + "next": ["next@16.1.6", "", { "dependencies": { "@next/env": "16.1.6", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.8.3", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.1.6", "@next/swc-darwin-x64": "16.1.6", "@next/swc-linux-arm64-gnu": "16.1.6", "@next/swc-linux-arm64-musl": "16.1.6", "@next/swc-linux-x64-gnu": "16.1.6", "@next/swc-linux-x64-musl": "16.1.6", "@next/swc-win32-arm64-msvc": "16.1.6", "@next/swc-win32-x64-msvc": "16.1.6", "sharp": "^0.34.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw=="], "next-auth": ["next-auth@5.0.0-beta.29", "", { "dependencies": { "@auth/core": "0.40.0" }, "peerDependencies": { "@simplewebauthn/browser": "^9.0.1", "@simplewebauthn/server": "^9.0.2", "next": "^14.0.0-0 || ^15.0.0-0", "nodemailer": "^6.6.5", "react": "^18.2.0 || ^19.0.0-0" }, "optionalPeers": ["@simplewebauthn/browser", "@simplewebauthn/server", "nodemailer"] }, "sha512-Ukpnuk3NMc/LiOl32njZPySk7pABEzbjhMUFd5/n10I0ZNC7NCuVv8IY2JgbDek2t/PUOifQEoUiOOTLy4os5A=="], diff --git a/package.json b/package.json index adf5f0a..66b3057 100755 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "db:migrate": "drizzle-kit migrate", "db:push": "drizzle-kit push", "db:studio": "drizzle-kit studio", - "db:seed": "bun scripts/seed-dev.ts", + "db:seed": "bun db:push && bun scripts/seed-dev.ts", "dev": "next dev --turbo", - "docker:up": "colima start && docker-compose up -d", - "docker:down": "docker-compose down && colima stop", + "docker:up": "if [ \"$(uname)\" = \"Darwin\" ]; then colima start; fi && docker compose up -d", + "docker:down": "docker compose down && if [ \"$(uname)\" = \"Darwin\" ]; then colima stop; fi", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", "format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", "lint": "next lint", @@ -67,7 +67,7 @@ "js-cookie": "^3.0.5", "lucide-react": "^0.536.0", "minio": "^8.0.6", - "next": "^16.0.10", + "next": "^16.1.6", "next-auth": "^5.0.0-beta.29", "next-themes": "^0.4.6", "postgres": "^3.4.4", @@ -88,6 +88,7 @@ "@eslint/eslintrc": "^3.3.1", "@tailwindcss/postcss": "^4.0.15", "@types/bcryptjs": "^3.0.0", + "@types/crypto-js": "^4.2.2", "@types/node": "^20.14.10", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", @@ -113,4 +114,4 @@ "sharp", "unrs-resolver" ] -} +} \ No newline at end of file diff --git a/robot-plugins b/robot-plugins index f3db314..c6310d3 160000 --- a/robot-plugins +++ b/robot-plugins @@ -1 +1 @@ -Subproject commit f3db314c8a1c2b3c1b77c6d1a8f67f3a52eed50e +Subproject commit c6310d3144d55b5e5455066fd1d35db426bfddb0 diff --git a/scripts/seed-dev.ts b/scripts/seed-dev.ts index d995a2a..6f21ec4 100755 --- a/scripts/seed-dev.ts +++ b/scripts/seed-dev.ts @@ -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" } diff --git a/scripts/verify-study-readiness.ts b/scripts/verify-study-readiness.ts index 2e4ab91..30c578e 100644 --- a/scripts/verify-study-readiness.ts +++ b/scripts/verify-study-readiness.ts @@ -47,10 +47,13 @@ async function verify() { // Verify Step Names const expectedSteps = ["The Hook", "The Narrative - Part 1", "Comprehension Check", "Positive Feedback", "Conclusion"]; for (let i = 0; i < expectedSteps.length; i++) { - if (steps[i].name !== expectedSteps[i]) { - console.error(`โŒ Step mismatch at index ${i}. Expected '${expectedSteps[i]}', got '${steps[i].name}'`); + const step = steps[i]; + if (!step) continue; + + if (step.name !== expectedSteps[i]) { + console.error(`โŒ Step mismatch at index ${i}. Expected '${expectedSteps[i]}', got '${step.name}'`); } else { - console.log(`โœ… Step ${i + 1}: ${steps[i].name}`); + console.log(`โœ… Step ${i + 1}: ${step.name}`); } } diff --git a/src/app/(dashboard)/nao-test/page.tsx b/src/app/(dashboard)/nao-test/page.tsx index 6896ccf..1c4ae07 100755 --- a/src/app/(dashboard)/nao-test/page.tsx +++ b/src/app/(dashboard)/nao-test/page.tsx @@ -161,7 +161,7 @@ export default function NaoTestPage() { data.topic?.includes("touch") || data.topic?.includes("sonar") ) { - setSensorData((prev) => ({ + setSensorData((prev: any) => ({ ...prev, [data.topic]: data.msg, })); @@ -196,14 +196,14 @@ export default function NaoTestPage() { const walkForward = () => { publishMessage("/cmd_vel", "geometry_msgs/Twist", { - linear: { x: walkSpeed[0], y: 0, z: 0 }, + linear: { x: walkSpeed[0] ?? 0, y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 }, }); }; const walkBackward = () => { publishMessage("/cmd_vel", "geometry_msgs/Twist", { - linear: { x: -walkSpeed[0], y: 0, z: 0 }, + linear: { x: -(walkSpeed[0] ?? 0), y: 0, z: 0 }, angular: { x: 0, y: 0, z: 0 }, }); }; @@ -211,14 +211,14 @@ export default function NaoTestPage() { const turnLeft = () => { publishMessage("/cmd_vel", "geometry_msgs/Twist", { linear: { x: 0, y: 0, z: 0 }, - angular: { x: 0, y: 0, z: turnSpeed[0] }, + angular: { x: 0, y: 0, z: turnSpeed[0] ?? 0 }, }); }; const turnRight = () => { publishMessage("/cmd_vel", "geometry_msgs/Twist", { linear: { x: 0, y: 0, z: 0 }, - angular: { x: 0, y: 0, z: -turnSpeed[0] }, + angular: { x: 0, y: 0, z: -(turnSpeed[0] ?? 0) }, }); }; @@ -232,7 +232,7 @@ export default function NaoTestPage() { const moveHead = () => { publishMessage("/joint_angles", "naoqi_bridge_msgs/JointAnglesWithSpeed", { joint_names: ["HeadYaw", "HeadPitch"], - joint_angles: [headYaw[0], headPitch[0]], + joint_angles: [headYaw[0] ?? 0, headPitch[0] ?? 0], speed: 0.3, }); }; @@ -365,7 +365,7 @@ export default function NaoTestPage() {
- +
- +
- +
- +
diff --git a/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/experiment-form.tsx b/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/experiment-form.tsx index e242051..bab7c1c 100644 --- a/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/experiment-form.tsx +++ b/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/experiment-form.tsx @@ -26,21 +26,17 @@ import { import { toast } from "sonner"; import { api } from "~/trpc/react"; import { useRouter } from "next/navigation"; -import { type Experiment } from "~/lib/experiments/types"; +import { type experiments, experimentStatusEnum } from "~/server/db/schema"; +import { type InferSelectModel } from "drizzle-orm"; + +type Experiment = InferSelectModel; const formSchema = z.object({ name: z.string().min(2, { message: "Name must be at least 2 characters.", }), description: z.string().optional(), - status: z.enum([ - "draft", - "ready", - "data_collection", - "analysis", - "completed", - "archived", - ]), + status: z.enum(experimentStatusEnum.enumValues), }); interface ExperimentFormProps { @@ -133,11 +129,9 @@ export function ExperimentForm({ experiment }: ExperimentFormProps) { Draft + Testing Ready - Data Collection - Analysis - Completed - Archived + Deprecated diff --git a/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/page.tsx b/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/page.tsx index b784f08..50eb50e 100644 --- a/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/page.tsx +++ b/src/app/(dashboard)/studies/[id]/experiments/[experimentId]/edit/page.tsx @@ -1,5 +1,8 @@ import { notFound } from "next/navigation"; -import { type Experiment } from "~/lib/experiments/types"; +import { type experiments } from "~/server/db/schema"; +import { type InferSelectModel } from "drizzle-orm"; + +type Experiment = InferSelectModel; import { api } from "~/trpc/server"; import { ExperimentForm } from "./experiment-form"; import { @@ -43,14 +46,6 @@ export default async function ExperimentEditPage({ title="Edit Experiment" subtitle={`Update settings for ${experiment.name}`} icon="Edit" - backButton={ - - } />
diff --git a/src/app/(dashboard)/studies/[id]/participants/[participantId]/page.tsx b/src/app/(dashboard)/studies/[id]/participants/[participantId]/page.tsx index 4171f88..8be144a 100644 --- a/src/app/(dashboard)/studies/[id]/participants/[participantId]/page.tsx +++ b/src/app/(dashboard)/studies/[id]/participants/[participantId]/page.tsx @@ -39,11 +39,10 @@ export default async function ParticipantDetailPage({ title={participant.participantCode} subtitle={participant.name ?? "Unnamed Participant"} icon="Users" - badge={ - - {participant.consentGiven ? "Consent Given" : "No Consent"} - - } + status={{ + label: participant.consentGiven ? "Consent Given" : "No Consent", + variant: participant.consentGiven ? "default" : "secondary" + }} actions={ - - - {actions.map((action) => ( + return ( + toggleCategory(category)} + > + - ))} - - - ); - })} + + + {actions.map((action) => ( + + ))} + + + ); + })}
@@ -962,7 +962,7 @@ export function RobotActionsPanel({ {/* Parameters */} {selectedAction?.parameters && - (selectedAction.parameters?.length ?? 0) > 0 ? ( + (selectedAction?.parameters?.length ?? 0) > 0 ? (
{selectedAction?.parameters?.map((param, index) => @@ -990,10 +990,10 @@ export function RobotActionsPanel({ className="w-full" > {selectedPluginData && - selectedAction && - executingActions.has( - `${selectedPluginData?.plugin.name}.${selectedAction?.id}`, - ) ? ( + selectedAction && + executingActions.has( + `${selectedPluginData?.plugin.name}.${selectedAction?.id}`, + ) ? ( <> Executing... diff --git a/src/lib/experiment-designer/block-converter.ts b/src/lib/experiment-designer/block-converter.ts index ee6c9ad..1510c6c 100755 --- a/src/lib/experiment-designer/block-converter.ts +++ b/src/lib/experiment-designer/block-converter.ts @@ -149,10 +149,10 @@ export function convertActionToDatabase( pluginVersion: action.source.pluginVersion, robotId: action.source.robotId, baseActionId: action.source.baseActionId, - transport: action.execution.transport, - ros2: action.execution.ros2, - rest: action.execution.rest, - retryable: action.execution.retryable, + transport: action.execution?.transport, + ros2: action.execution?.ros2, + rest: action.execution?.rest, + retryable: action.execution?.retryable, parameterSchemaRaw: action.parameterSchemaRaw, sourceKind: action.source.kind, category: action.category, diff --git a/src/lib/experiment-designer/visual-design-guard.ts b/src/lib/experiment-designer/visual-design-guard.ts index c0da880..fe93c53 100755 --- a/src/lib/experiment-designer/visual-design-guard.ts +++ b/src/lib/experiment-designer/visual-design-guard.ts @@ -267,7 +267,7 @@ export function parseVisualDesignSteps(raw: unknown): { if (!act.source.kind) { issues.push(`Action "${act.id}" missing source.kind`); } - if (!act.execution.transport) { + if (!act.execution?.transport) { issues.push(`Action "${act.id}" missing execution transport`); } } diff --git a/src/server/api/routers/robots.ts b/src/server/api/routers/robots.ts index 3047b5e..39f8b55 100755 --- a/src/server/api/routers/robots.ts +++ b/src/server/api/routers/robots.ts @@ -471,6 +471,7 @@ export const robotsRouter = createTRPCRouter({ actionDefinitions: plugins.actionDefinitions, createdAt: plugins.createdAt, updatedAt: plugins.updatedAt, + metadata: plugins.metadata, }, installation: { id: studyPlugins.id,