From 497ee7e736a0adeb7f904feb0a030737e1babe85 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Tue, 9 Dec 2025 11:15:18 -0500 Subject: [PATCH 1/3] Add member profiles with agent discovery and rich agent card display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement member profile management (create, edit, view, delete) - Add public member directory with search and filtering - Create shared agent card component with full details (type, protocols, stats) - Implement agent discovery API with protocol detection (MCP/A2A) - Fetch type-specific stats: formats for creative agents, products/publishers for sales - Display agent info consistently across edit profile, member detail, and member cards - Add member-to-agent relationships with visibility control - Support multiple agents per member with public/private visibility toggle - Add database schema for member profiles and agent configurations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- conductor.json | 2 +- server/public/dashboard.html | 1082 ++++++++---- server/public/index.html | 36 +- server/public/member-card.js | 608 +++++++ server/public/member-profile.html | 1525 +++++++++++++++++ server/public/members.html | 1518 ++++++++++++++++ server/public/nav.js | 302 +++- server/public/shared-nav.html | 95 - server/public/team.html | 55 +- server/src/db/member-db.ts | 359 ++++ .../src/db/migrations/011_member_profiles.sql | 64 + .../db/migrations/012_member_agent_urls.sql | 7 + .../013_subscription_status_columns.sql | 20 + .../src/db/migrations/014_agent_configs.sql | 33 + server/src/db/registry-db.ts | 72 +- server/src/env-validation.ts | 5 + server/src/http.ts | 1420 ++++++++++++++- server/src/types.ts | 106 ++ server/tests/unit/agreement-recording.test.ts | 351 ++++ server/tests/unit/database-schema.test.ts | 313 ++++ .../tests/unit/webhook-subscription.test.ts | 302 ++++ 21 files changed, 7698 insertions(+), 577 deletions(-) create mode 100644 server/public/member-card.js create mode 100644 server/public/member-profile.html create mode 100644 server/public/members.html delete mode 100644 server/public/shared-nav.html create mode 100644 server/src/db/member-db.ts create mode 100644 server/src/db/migrations/011_member_profiles.sql create mode 100644 server/src/db/migrations/012_member_agent_urls.sql create mode 100644 server/src/db/migrations/013_subscription_status_columns.sql create mode 100644 server/src/db/migrations/014_agent_configs.sql create mode 100644 server/tests/unit/agreement-recording.test.ts create mode 100644 server/tests/unit/database-schema.test.ts create mode 100644 server/tests/unit/webhook-subscription.test.ts diff --git a/conductor.json b/conductor.json index 8dc2dbc695..ac7b82ac50 100644 --- a/conductor.json +++ b/conductor.json @@ -7,6 +7,6 @@ } ], "scripts": { - "setup": "git config core.hooksPath .husky && npm install && uv sync && open -a Docker && sleep 5 && docker compose up -d postgres && sleep 2 && DB_PORT=$(docker compose port postgres 5432 | cut -d: -f2) && APP_PORT=$((50000 + RANDOM % 10000)) && echo \"DATABASE_URL=postgresql://adcp:localdev@localhost:$DB_PORT/adcp_registry\" >> .env.local && echo \"DATABASE_SSL=false\" >> .env.local && echo \"CONDUCTOR_PORT=$APP_PORT\" >> .env.local && npm run db:migrate" + "setup": "git config core.hooksPath .husky && npm install && uv sync && open -a Docker && sleep 5 && docker compose up -d postgres && sleep 2 && DB_PORT=$(docker compose port postgres 5432 | cut -d: -f2) && APP_PORT=$((50000 + RANDOM % 10000)) && echo \"DATABASE_URL=postgresql://adcp:localdev@localhost:$DB_PORT/adcp_registry\" >> .env.local && echo \"DATABASE_SSL=false\" >> .env.local && echo \"CONDUCTOR_PORT=$APP_PORT\" >> .env.local && cat ../../.env >> .env.local && npm run db:migrate" } } diff --git a/server/public/dashboard.html b/server/public/dashboard.html index 54b77379b3..c9baed2ed3 100644 --- a/server/public/dashboard.html +++ b/server/public/dashboard.html @@ -12,57 +12,18 @@ background: #f5f5f5; min-height: 100vh; } - .header { - background: white; + .dashboard-toolbar { + background: #f9fafb; border-bottom: 1px solid #e0e0e0; - padding: 20px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); + padding: 10px 20px; } - .header-content { + .dashboard-toolbar-content { max-width: 1200px; margin: 0 auto; display: flex; - justify-content: space-between; + justify-content: flex-start; align-items: center; } - .logo { - font-size: 24px; - font-weight: 700; - color: #667eea; - } - .user-menu { - display: flex; - align-items: center; - gap: 20px; - } - .user-info { - color: #666; - } - .btn-admin { - padding: 8px 16px; - background: #667eea; - border: none; - border-radius: 6px; - cursor: pointer; - text-decoration: none; - color: white; - font-weight: 600; - } - .btn-admin:hover { - background: #5568d3; - } - .btn-logout { - padding: 8px 16px; - background: #f5f5f5; - border: none; - border-radius: 6px; - cursor: pointer; - text-decoration: none; - color: #333; - } - .btn-logout:hover { - background: #e0e0e0; - } .container { max-width: 1200px; margin: 40px auto; @@ -166,9 +127,9 @@ .btn-secondary:hover { background: #d0d0d0; } + /* Org Switcher */ .org-switcher { position: relative; - margin-right: 15px; } .org-switcher-btn { display: flex; @@ -176,94 +137,84 @@ gap: 8px; padding: 8px 12px; background: #f5f5f5; - border: 1px solid #ddd; + border: 1px solid #e0e0e0; border-radius: 6px; cursor: pointer; font-size: 14px; color: #333; + min-width: 180px; } .org-switcher-btn:hover { - background: #e0e0e0; + background: #e8e8e8; } - .org-switcher-icon { - font-size: 18px; - } - .org-switcher-name { - max-width: 200px; + .org-switcher-btn .org-name { + flex: 1; + text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } - .org-switcher-badge { - display: inline-block; - padding: 2px 6px; - border-radius: 8px; + .org-switcher-btn .arrow { font-size: 10px; - margin-left: 5px; + color: #666; } - .org-switcher-badge.personal { background: #e3f2fd; color: #1565c0; } - .org-switcher-badge.team { background: #e8f5e9; color: #2e7d32; } - .org-dropdown { + .org-switcher-dropdown { display: none; position: absolute; top: 100%; + left: 0; right: 0; margin-top: 4px; background: white; - border: 1px solid #ddd; - border-radius: 8px; + border: 1px solid #e0e0e0; + border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); - min-width: 280px; z-index: 100; + max-height: 300px; + overflow-y: auto; } - .org-dropdown.show { + .org-switcher-dropdown.show { display: block; } - .org-dropdown-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; + .org-switcher-item { + padding: 10px 12px; cursor: pointer; border-bottom: 1px solid #f0f0f0; } - .org-dropdown-item:last-child { + .org-switcher-item:last-child { border-bottom: none; } - .org-dropdown-item:hover { - background: #f9f9f9; + .org-switcher-item:hover { + background: #f5f5f5; } - .org-dropdown-item.active { - background: #f0f3ff; + .org-switcher-item.selected { + background: #e3f2fd; } - .org-dropdown-name { + .org-switcher-item .item-name { font-weight: 500; color: #333; } - .org-dropdown-role { - font-size: 12px; - color: #666; - } - .org-dropdown-header { - padding: 10px 16px; - font-size: 12px; - color: #666; - font-weight: 600; - text-transform: uppercase; - border-bottom: 1px solid #e0e0e0; - background: #fafafa; + .org-switcher-item .item-badge { + display: inline-block; + padding: 2px 6px; + font-size: 10px; + border-radius: 10px; + margin-left: 6px; } - .org-dropdown-footer { - padding: 12px 16px; - border-top: 1px solid #e0e0e0; + .org-switcher-item .item-badge.active { + background: #d1fae5; + color: #065f46; } - .org-dropdown-footer a { - color: #667eea; - text-decoration: none; - font-size: 14px; + .org-switcher-item .item-badge.inactive { + background: #f3f4f6; + color: #6b7280; } - .org-dropdown-footer a:hover { - text-decoration: underline; + + /* Responsive layout for dashboard */ + @media (max-width: 900px) { + .dashboard-grid { + grid-template-columns: 1fr !important; + } } @@ -285,53 +236,51 @@

⚠️ Agreement Update