Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
node_modules/
frontend/
backend/
dist/
build/
**/build/
coverage/
# Desktop overlay has its own build/lint setup
desktop-overlay/
# Browser/ESM-only packages with their own tooling
packages/
# Service-specific public assets (browser JS, not Node)
services/heady-auth/public/
# Notebooks (prototype/experimental)
notebooks/
# ESM client libraries (browser-targeted, not Node.js modules)
src/heady-auth-client.js
src/heady-auth-service.mjs
58 changes: 50 additions & 8 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,58 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"env": {
"node": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
"eslint:recommended"
],
"parserOptions": {
"ecmaVersion": 2021,
"sourceType": "commonjs"
},
"rules": {
"no-restricted-syntax": ["error", {
"selector": "Literal[value=/api.headysystems.com|127\.0\.0\.1/]",
"message": "Use internal.headyio.com or api.headyio.com instead of api.headysystems.com/api.headysystems.com"
"selector": "Literal[value=/api\\.headysystems\\.com/]",
"message": "Use internal.headyio.com or api.headyio.com instead of api.headysystems.com"
}],
"no-eval": "error",
"no-unused-vars": ["error", {
"vars": "all",
"args": "after-used",
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true
}]
}
},
"overrides": [
{
"files": ["tests/**/*.js", "**/*.test.js", "**/*.spec.js"],
"env": { "jest": true }
},
{
"files": [
"workers/**/*.js",
"src/workers/**/*.js",
"workers/heady-sync/src/**/*.js"
],
"parserOptions": {
"sourceType": "module"
},
"env": { "worker": true }
},
{
"files": [
"packages/heady-music/max-for-live/**/*.js"
],
"globals": {
"autowatch": "writable",
"inlets": "writable",
"outlets": "writable",
"post": "readonly",
"outlet": "readonly",
"LiveAPI": "readonly"
}
}
]
}

117 changes: 30 additions & 87 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<<<<<<< HEAD
# HEADY_BRAND:BEGIN
# HEADY SYSTEMS :: SACRED GEOMETRY
# FILE: .gitignore
Expand All @@ -17,12 +16,18 @@
node_modules/
venv/
.venv/
env/
__pycache__/
*.pyc
*.py[cod]
*.pyo
*.pyd
*.egg-info/

# === Build Output ===
dist/
build/
.next/
**/build/
**/out/
*.js.map
Expand All @@ -33,7 +38,12 @@ build/
.env.local
.env.production
.env.staging
.env.hybrid
.heady_secrets
.heady/memory.db
.heady/*.db
*.db
.heady/.heady/
*.pem
*.key
*.p12
Expand All @@ -42,11 +52,17 @@ build/
# === Logs ===
*.log
logs/
audit_logs.jsonl
.heady_deploy_log.jsonl
*.jsonl

# === Testing & Cache ===
.pytest_cache/
.ipynb_checkpoints/
coverage/
.nyc_output/
pipeline-output/
.heady_cache/

# === IDE & System ===
.vscode/
Expand All @@ -68,99 +84,26 @@ HeadyAcademy/Content_Forge/Visualizations/
*.exe
*.msi
*.dmg
=======
# Dependencies
node_modules/
__pycache__/
*.pyc
venv/
.venv/

# Build output
dist/
build/
.next/

# Environment
.env
.env.local
.env.production
.heady_secrets

# Logs
*.log
logs/

# IDE
.idea/
.vscode/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Python
<<<<<<< HEAD
__pycache__/
*.py[cod]
*.pyo
*.pyd
*.pyc
.venv/
env/
venv/
.pytest_cache/
*.egg-info/
.ipynb_checkpoints/

# Data (raw and external, to avoid huge files in git)
data/raw/
data/external/
=======
.pytest_cache/
*.egg-info/
>>>>>>> a3d7d06c432bf92df85e53f8d0cf1e6c8622ccea
AndroidSDK/system-images/**/*.img

# Generated
heady-manifest.json
HeadyAcademy/Content_Forge/Visualizations/
pipeline-output/
.heady_cache/
<<<<<<< HEAD
# === Deploy packages (temp build artifacts) ===
deploy-package-*/
offline-packages/

# Standalone repos (separate git projects)
# === Standalone repos (separate git projects) ===
windsurf-next/


# Personal notebooks (experimental, not tracked)
# === Personal notebooks (experimental, not tracked) ===
notebooks/personal/*
!notebooks/personal/.gitkeep

# Secrets
.heady/memory.db
.heady/*.db
*.db
.heady/.heady/

# Files exceeding GitHub LFS 2GB per-file limit
AndroidSDK/system-images/**/*.img

# Deploy packages (temp build artifacts)
deploy-package-*/
offline-packages/
# === Runtime artifacts ===
*.pid
*.bak

# Local Netlify folder
# === Local Netlify folder ===
.netlify

# Security β€” sensitive files (added by deep scan remediation)
.env.hybrid
*.pid
*.bak
audit_logs.jsonl
.heady_deploy_log.jsonl
*.jsonl
=======
>>>>>>> a3d7d06c432bf92df85e53f8d0cf1e6c8622ccea
>>>>>>> origin/main
# === Data (raw and external, avoid huge files in git) ===
data/raw/
data/external/
2 changes: 0 additions & 2 deletions heady-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
*/

const { CommandRegistry } = require('./src/command-registry');
const path = require('path');

const PHI = 1.618033988749895;

Expand Down Expand Up @@ -112,7 +111,6 @@ async function main() {

// ── Execute shortcut command ──
const shortcut = args[0];
const cmdArgs = args.slice(1);

try {
const command = registry.resolveShortcut(shortcut);
Expand Down
38 changes: 30 additions & 8 deletions heady-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,26 @@ const { logger } = require(path.join(__dirname, "src", "structured_logger"));
const shutdown = new GracefulShutdownManager({ timeout: 34000 });

const PORT = Number(process.env.PORT || 3300);
const HEADY_ADMIN_SCRIPT = process.env.HEADY_ADMIN_SCRIPT || path.join(__dirname, "src", "heady_project", "heady_conductor.py");
const HEADY_PYTHON_BIN = process.env.HEADY_PYTHON_BIN || "python";

const app = express();
app.use(shutdown.middleware()); // 503 during drain
app.use(logger.requestLogger()); // Structured JSON request logging
app.use(express.json({ limit: "50mb" }));
app.use(cors());
app.use(express.json({ limit: "10mb" }));

// Restrict CORS to known origins; fall back to same-origin in production
const ALLOWED_ORIGINS = process.env.CORS_ALLOWED_ORIGINS
? process.env.CORS_ALLOWED_ORIGINS.split(",").map(o => o.trim())
: ["http://localhost:3000", "http://localhost:3300"];

app.use(cors({
origin: (origin, callback) => {
// Allow requests with no origin (e.g., curl, server-to-server)
if (!origin) return callback(null, true);
if (ALLOWED_ORIGINS.includes(origin)) return callback(null, true);
callback(new Error(`CORS policy: origin '${origin}' not allowed`));
},
credentials: true,
}));

function readJsonFileSafe(filePath) {
try {
Expand Down Expand Up @@ -97,6 +109,16 @@ app.get("/api/maid/inventory", (req, res) => {
res.json(inventory);
});

// Input validation helper β€” rejects strings with shell-metacharacters or flag-injection patterns
function validateConductorInput(value, fieldName) {
if (typeof value !== "string") throw new Error(`${fieldName} must be a string`);
if (value.length > 1000) throw new Error(`${fieldName} exceeds maximum length of 1000 characters`);
// Reject values that start with "-" (would be interpreted as CLI flags)
if (value.startsWith("-")) throw new Error(`${fieldName} must not start with a hyphen`);
// Reject shell metacharacters that could be used for injection
if (/[;&|`$<>\\]/.test(value)) throw new Error(`${fieldName} contains disallowed characters`);
}

// HeadyConductor API Endpoints
app.post("/api/nexus/route", async (req, res) => {
try {
Expand All @@ -113,7 +135,7 @@ app.post("/api/conductor/orchestrate", async (req, res) => {
if (!request) {
return res.status(400).json({ error: "Request parameter required" });
}

validateConductorInput(request, "request");
const result = await runPythonConductor(["--request", request]);
res.json(result);
} catch (error) {
Expand Down Expand Up @@ -145,7 +167,7 @@ app.get("/api/conductor/query", async (req, res) => {
if (!q) {
return res.status(400).json({ error: "Query parameter 'q' required" });
}

validateConductorInput(q, "q");
const result = await runPythonConductor(["--query", q]);
res.json(result);
} catch (error) {
Expand All @@ -159,7 +181,7 @@ app.post("/api/conductor/workflow", async (req, res) => {
if (!workflow) {
return res.status(400).json({ error: "Workflow parameter required" });
}

validateConductorInput(workflow, "workflow");
const result = await runPythonConductor(["--workflow", workflow]);
res.json(result);
} catch (error) {
Expand All @@ -173,7 +195,7 @@ app.post("/api/conductor/node", async (req, res) => {
if (!node) {
return res.status(400).json({ error: "Node parameter required" });
}

validateConductorInput(node, "node");
const result = await runPythonConductor(["--node", node]);
res.json(result);
} catch (error) {
Expand Down
Loading