-
Notifications
You must be signed in to change notification settings - Fork 29
feat: Stellar Wave — responsive, accessibility, load tests, security, repo map (#34, #35, #38, #50, #48) #111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
f9982a4
bdbe70a
ec330a0
9aebe66
6f77754
497c875
c92754a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # Security Checklist for Contributors — Closes #50 | ||
|
|
||
| Review before pushing code. Supplements [CONTRIBUTING.md](./CONTRIBUTING.md). | ||
|
|
||
| ## Before Opening a Pull Request | ||
|
|
||
| ### Secrets & Credentials | ||
| - [ ] No API keys, tokens, or passwords in source code | ||
| - [ ] Environment variables used for all secrets (see `.env.example`) | ||
| - [ ] `.env` and `.env.local` are in `.gitignore` | ||
| - [ ] No hardcoded JWT secrets or signing keys | ||
|
|
||
| ### Dependencies | ||
| - [ ] Run `npm audit` — no critical or high vulnerabilities | ||
| - [ ] New dependencies reviewed for maintenance status | ||
| - [ ] Lockfile (`package-lock.json`) updated | ||
| - [ ] No deprecated packages introduced | ||
|
|
||
| ### Input Validation | ||
| - [ ] All user inputs validated server-side | ||
| - [ ] SQL/NoSQL injection protections in place | ||
| - [ ] File upload paths sanitized and size-limited | ||
| - [ ] XSS protections: output encoding, CSP headers | ||
|
|
||
| ### Authentication & Authorization | ||
| - [ ] Endpoints gated with auth middleware | ||
| - [ ] Role-based access control enforced | ||
| - [ ] Session tokens use secure, httpOnly, SameSite flags | ||
| - [ ] Rate limiting on auth endpoints | ||
|
|
||
| ### Data Protection | ||
| - [ ] Sensitive data encrypted at rest | ||
| - [ ] PII minimized | ||
| - [ ] Logging does not capture passwords, tokens, or PII | ||
| - [ ] CORS configured with specific origins (not `*`) | ||
|
|
||
| ### API Security | ||
| - [ ] API responses don't leak stack traces | ||
| - [ ] Pagination limits on list endpoints | ||
| - [ ] Content-Type headers validated | ||
| - [ ] HTTPS enforced (HSTS header) | ||
|
|
||
| ### Testing | ||
| - [ ] Security-focused test cases included | ||
| - [ ] Edge cases tested (empty input, max-length, special chars) | ||
| - [ ] Error paths tested | ||
|
|
||
| ## CI/CD Pipeline | ||
| - [ ] `npm test` passes locally and in CI | ||
| - [ ] `npm run lint` passes with no errors | ||
| - [ ] Git hooks (husky) pass pre-commit checks | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| # Stellarmind Repository Map — Closes #48 | ||
|
|
||
| A guided tour for new contributors. | ||
|
|
||
| ## Top-Level Layout | ||
|
|
||
| ``` | ||
| stellarmind/ | ||
| ├── .github/ # CI workflows, issue templates | ||
| ├── .husky/ # Git hooks (pre-commit checks) | ||
| ├── docs/ # Project documentation | ||
| ├── public/ # Static assets and entry HTML | ||
| │ ├── assets/ # CSS, JS, images | ||
| │ │ ├── css/ # Stylesheets | ||
| │ │ └── js/ # Client-side JavaScript | ||
| │ └── index.html # Main entry point | ||
| ├── src/ # Application source code | ||
| │ ├── agents/ # AI agent orchestration modules | ||
| │ ├── api/ # REST API route handlers | ||
| │ ├── middleware/ # Express middleware (auth, validation) | ||
| │ ├── pricing/ # Pricing engine and calculators | ||
| │ ├── orchestrator/ # Multi-agent coordination logic | ||
| │ └── utils/ # Shared utilities and helpers | ||
| ├── tests/ # Test suites | ||
| │ └── load/ # Load test scenarios (k6, Artillery) | ||
| ├── package.json # Node.js dependencies and scripts | ||
| ├── vercel.json # Vercel deployment configuration | ||
| ├── CONTRIBUTING.md # Contribution guidelines | ||
| ├── SECURITY.md # Security policy | ||
| └── README.md # Project overview | ||
| ``` | ||
|
|
||
| ## Key Directories | ||
|
|
||
| ### `src/agents/` | ||
| Each agent module handles a specific AI task. Agents communicate through the | ||
| orchestrator. See `src/orchestrator/` for the coordination layer. | ||
|
|
||
| ### `src/api/` | ||
| Express route handlers. New endpoints should be registered here and follow | ||
| REST conventions. Each route file exports a router. | ||
|
|
||
| ### `src/pricing/` | ||
| The pricing engine that calculates costs based on agent usage, token | ||
| consumption, and plan tiers. See `PRICING_INDEX.md` for business logic. | ||
|
|
||
| ### `tests/` | ||
| - `*.test.js` — Unit and integration tests (Jest) | ||
| - `load/` — Performance and load tests (k6, Artillery) | ||
|
|
||
| ## Quick Start (New Contributors) | ||
|
|
||
| 1. **Read**: `CONTRIBUTING.md` and this repository map | ||
| 2. **Setup**: `nvm use && npm install` | ||
| 3. **Develop**: `npm run dev` starts the development server | ||
| 4. **Test**: `npm test` runs the full test suite | ||
| 5. **Lint**: `npm run lint` checks code style | ||
| 6. **Submit**: Open a PR following the PR template | ||
|
|
||
| ## Common Workflows | ||
|
|
||
| | Task | Command | | ||
| |------|---------| | ||
| | Start dev server | `npm run dev` | | ||
| | Run all tests | `npm test` | | ||
| | Run single test | `npx jest path/to/test` | | ||
| | Lint code | `npm run lint` | | ||
| | Build for production | `npm run build` | | ||
|
|
||
| ## Architecture Overview | ||
|
|
||
| Stellarmind uses an **orchestrator pattern** where a central coordination | ||
| module dispatches tasks to specialized AI agents. Agents are stateless; | ||
| state is managed at the orchestrator level and persisted via the API layer. | ||
|
|
||
| For detailed architecture, see `FRONTEND_STRUCTURE.md` and the inline | ||
| documentation in `src/orchestrator/`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /* Accessibility Improvements — Closes #35 */ | ||
| .skip-link { | ||
| position: absolute; | ||
| top: -40px; | ||
| left: 6px; | ||
| background: var(--bg-primary, #fff); | ||
| color: var(--text-primary, #000); | ||
| padding: 8px 16px; | ||
| z-index: 10000; | ||
| border-radius: 0 0 4px 4px; | ||
| text-decoration: none; | ||
| font-weight: 600; | ||
| transition: top 0.2s ease; | ||
| } | ||
| .skip-link:focus { | ||
| top: 0; | ||
| } | ||
| :focus-visible { | ||
| outline: 3px solid var(--focus-color, #4A90D9); | ||
| outline-offset: 2px; | ||
| border-radius: 2px; | ||
| } | ||
| :focus:not(:focus-visible) { | ||
| outline: none; | ||
| } | ||
| .sr-only { | ||
| position: absolute; | ||
| width: 1px; | ||
| height: 1px; | ||
| padding: 0; | ||
| margin: -1px; | ||
| overflow: hidden; | ||
| clip: rect(0, 0, 0, 0); | ||
| white-space: nowrap; | ||
| border-width: 0; | ||
| } | ||
| @media (prefers-contrast: high) { | ||
| :root { | ||
| --border-color: #000; | ||
| --text-primary: #000; | ||
| --bg-primary: #fff; | ||
| } | ||
| button, .btn, input, select, textarea { | ||
| border: 2px solid #000; | ||
| } | ||
| } | ||
| @media (prefers-reduced-motion: reduce) { | ||
| *, *::before, *::after { | ||
| animation-duration: 0.01ms !important; | ||
| animation-iteration-count: 1 !important; | ||
| transition-duration: 0.01ms !important; | ||
| scroll-behavior: auto !important; | ||
| } | ||
| } | ||
| button, .btn, [role="button"], a.nav-link, .clickable { | ||
| min-height: 44px; | ||
| min-width: 44px; | ||
| } | ||
| .text-muted { | ||
| color: #666 !important; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| /* Mobile Responsiveness Improvements — Closes #34 */ | ||
| /* Sidebar collapse on tablets */ | ||
| @media (max-width: 1024px) { | ||
| .sidebar { | ||
| width: 60px; | ||
| overflow: hidden; | ||
| } | ||
| .sidebar:hover, | ||
| .sidebar:focus-within { | ||
| width: 240px; | ||
| } | ||
| .sidebar .nav-label { | ||
| display: none; | ||
| } | ||
| .sidebar:hover .nav-label, | ||
| .sidebar:focus-within .nav-label { | ||
| display: inline; | ||
| } | ||
| .main-content { | ||
| margin-left: 60px; | ||
| } | ||
| } | ||
|
|
||
| /* Full mobile collapse */ | ||
| @media (max-width: 768px) { | ||
| .sidebar { | ||
| transform: translateX(-100%); | ||
| position: fixed; | ||
| z-index: 1000; | ||
| height: 100vh; | ||
| transition: transform 0.3s ease; | ||
| } | ||
| .sidebar.open { | ||
| transform: translateX(0); | ||
| width: 240px; | ||
| } | ||
| .sidebar.open .nav-label { | ||
| display: inline; | ||
| } | ||
| .main-content { | ||
| margin-left: 0; | ||
| padding: 1rem; | ||
| } | ||
| .mobile-menu-toggle { | ||
| display: block; | ||
| } | ||
|
Comment on lines
+25
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 3 --glob '*.js' --glob '*.html' \
'mobile-menu-toggle|classList\.(toggle|add|remove)\([^)]*["'\'']open|aria-expanded' publicRepository: Flamki/stellarmind Length of output: 156 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf 'Tracked public JS/HTML/CSS files:\n'
git ls-files public | sed -n '1,200p'
printf '\nCSS file excerpt:\n'
sed -n '1,80p' public/assets/css/responsive.css 2>/dev/null || true
printf '\nSearch relevant tokens (all tracked text files):\n'
rg -n -C 3 'mobile-menu-toggle|sidebar\.open|classList\.(toggle|add|remove)\([^)]*["'\'']open|aria-expanded|aria-controls|onclick|addEventListener' public || trueRepository: Flamki/stellarmind Length of output: 10929 🌐 Web query:
💡 Result: The terms provided relate to a common pattern for implementing a responsive sidebar menu in web development, often found in projects like those managed under the Flamki organization on GitHub [1][2][3]. The file path public/assets/css/responsive.css typically contains the CSS rules for handling sidebar visibility on smaller screens, such as using media queries to toggle the display or transform properties (e.g., translateX) [2][4][3]. The terms mobile-menu-toggle and sidebar.open refer to the common JavaScript and CSS implementation where: 1. mobile-menu-toggle: Acts as a trigger element (often a hamburger icon or button) that captures click events [2][5][3]. 2. sidebar.open: A CSS class added or removed from the sidebar element via JavaScript to control its visibility (e.g., shifting it into or out of the viewport) [6][5][3]. While Flamki maintains several repositories (such as stellarmind, a marketplace for AI agents on Stellar) [1][7][8], these specific file paths and class names are standard conventions used across many web projects for mobile-responsive navigation rather than being unique to a single specific codebase [2][6][5][3]. Citations:
Wire the mobile toggle so CSS can reveal the sidebar.
🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| /* Table responsiveness */ | ||
| @media (max-width: 768px) { | ||
| table { | ||
| display: block; | ||
| overflow-x: auto; | ||
| white-space: nowrap; | ||
| } | ||
| .dashboard-grid { | ||
| grid-template-columns: 1fr; | ||
| } | ||
| .card { | ||
| min-width: 0; | ||
| } | ||
| } | ||
|
|
||
| /* Print styles */ | ||
| @media print { | ||
| .sidebar, .mobile-menu-toggle, .no-print { | ||
| display: none !important; | ||
| } | ||
| .main-content { | ||
| margin-left: 0 !important; | ||
| } | ||
| } | ||
|
|
||
| /* Mobile menu toggle */ | ||
| .mobile-menu-toggle { | ||
| display: none; | ||
| position: fixed; | ||
| top: 1rem; | ||
| left: 1rem; | ||
| z-index: 1001; | ||
| background: var(--bg-primary, #fff); | ||
| border: 1px solid var(--border-color, #ddd); | ||
| border-radius: 4px; | ||
| padding: 0.5rem; | ||
| cursor: pointer; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // Accessibility enhancements — Closes #35 | ||
| (function () { | ||
| 'use strict'; | ||
|
|
||
| function injectSkipLink() { | ||
| var skipLink = document.createElement('a'); | ||
| skipLink.href = '#main-content'; | ||
| skipLink.className = 'skip-link'; | ||
| skipLink.textContent = 'Skip to main content'; | ||
| document.body.prepend(skipLink); | ||
| } | ||
|
|
||
| function injectLiveRegion() { | ||
| var region = document.createElement('div'); | ||
| region.id = 'aria-live-region'; | ||
| region.className = 'sr-only'; | ||
| region.setAttribute('aria-live', 'polite'); | ||
| region.setAttribute('aria-atomic', 'true'); | ||
| document.body.appendChild(region); | ||
| } | ||
|
|
||
| function setupFocusTraps() { | ||
| var observer = new MutationObserver(function (mutations) { | ||
| mutations.forEach(function (mutation) { | ||
| mutation.addedNodes.forEach(function (node) { | ||
| if (node.nodeType !== 1) return; | ||
| var dialog = node.matches && node.matches('[role="dialog"], dialog') | ||
| ? node | ||
| : node.querySelector && node.querySelector('[role="dialog"], dialog'); | ||
| if (!dialog) return; | ||
|
|
||
| var focusable = dialog.querySelectorAll( | ||
| 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' | ||
| ); | ||
| if (focusable.length === 0) return; | ||
|
|
||
| var first = focusable[0]; | ||
| var last = focusable[focusable.length - 1]; | ||
|
|
||
| dialog.addEventListener('keydown', function (e) { | ||
| if (e.key !== 'Tab') return; | ||
| if (e.shiftKey && document.activeElement === first) { | ||
| e.preventDefault(); | ||
| last.focus(); | ||
| } else if (!e.shiftKey && document.activeElement === last) { | ||
| e.preventDefault(); | ||
| first.focus(); | ||
| } | ||
| }); | ||
|
|
||
| setTimeout(function () { first.focus(); }, 100); | ||
| }); | ||
| }); | ||
| }); | ||
|
|
||
| observer.observe(document.body, { childList: true, subtree: true }); | ||
|
Comment on lines
+22
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Attach the focus trap to dialogs that already exist. Line 56 observes only dialogs added after initialization. 🧰 Tools🪛 ast-grep (0.45.0)[error] 50-50: React's useState should not be directly called (usestate-direct-usage) 🪛 ESLint[error] 26-26: Delete (prettier/prettier) [error] 27-27: Insert (prettier/prettier) [error] 28-28: Insert (prettier/prettier) [error] 29-29: Replace (prettier/prettier) [error] 30-30: Delete (prettier/prettier) [error] 34-34: Delete (prettier/prettier) [error] 35-35: Delete (prettier/prettier) [error] 37-37: Delete (prettier/prettier) [error] 38-38: Delete (prettier/prettier) [error] 41-41: Delete (prettier/prettier) [error] 43-43: Delete (prettier/prettier) [error] 44-44: Delete (prettier/prettier) [error] 46-46: Delete (prettier/prettier) [error] 47-47: Delete (prettier/prettier) [error] 49-49: Delete (prettier/prettier) [error] 51-51: Replace (prettier/prettier) [error] 52-52: Delete (prettier/prettier) [error] 53-53: Delete (prettier/prettier) [error] 54-54: Delete (prettier/prettier) [error] 56-56: Delete (prettier/prettier) 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| if (document.readyState === 'loading') { | ||
| document.addEventListener('DOMContentLoaded', init); | ||
| } else { | ||
| init(); | ||
| } | ||
|
|
||
| function init() { | ||
| injectSkipLink(); | ||
| injectLiveRegion(); | ||
| setupFocusTraps(); | ||
|
Comment on lines
+65
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 3 'accessibility\.(css|js)|responsive\.css' publicRepository: Flamki/stellarmind Length of output: 156 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "Tracked files under public:"
git ls-files public | sed -n '1,200p'
echo
echo "Candidate files of likely interest:"
git ls-files public | grep -Ei '(dashboard|layout|template|accessibility|responsive|\.css|\.js)$' | sed -n '1,200p'
echo
echo "All accessibility/responsive/search terms in tracked repo files:"
rg -n -C 2 'accessibility|responsive|skip-link|focus-trap|aria-live|live-region|skip link|Live Region|focus trap' . | sed -n '1,240p'Repository: Flamki/stellarmind Length of output: 6494 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "index.html head and script tags:"
rg -n -C 4 '<head>|</head>|<link|<script|init\(\\)|init\\(' public/index.html
echo
echo "Tracked files with public/index.html:"
python3 - <<'PY'
from pathlib import Path
for p in Path('.').rglob('*'):
if p.is_file():
try:
s=p.read_text(errors='ignore')
except Exception:
continue
if 'public/index.html' in s or s.count('index.html') > 0:
continue
if 'public/assets/css/accessibility.css' in s or 'public/assets/js/accessibility.js' in s or 'responsive.css' in s:
print(p)
PYRepository: Flamki/stellarmind Length of output: 1406 Register the new accessibility assets in the dashboard HTML.
🧰 Tools🪛 ESLint[error] 66-66: Delete (prettier/prettier) [error] 67-67: Delete (prettier/prettier) [error] 68-68: Delete (prettier/prettier) 🤖 Prompt for AI Agents |
||
| } | ||
| })(); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Align contributor documentation with the repository layout.
These documents use paths that can send contributors to missing or inactive locations.
docs/CONTRIBUTING_SECURITY.md#L3-L3: change./CONTRIBUTING.mdto../CONTRIBUTING.mdif the root-level file is intended.docs/REPOSITORY_MAP.md#L17-L23: document the actual locations of route and validation code, includingsrc/server.jsandsrc/requestValidation.js.docs/REPOSITORY_MAP.md#L39-L41: align thesrc/api/guidance with the actual route-registration boundary.📍 Affects 2 files
docs/CONTRIBUTING_SECURITY.md#L3-L3(this comment)docs/REPOSITORY_MAP.md#L17-L23docs/REPOSITORY_MAP.md#L39-L41