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
61 changes: 54 additions & 7 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,63 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"env": {
"node": true,
"es2022": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
"eslint:recommended"
],
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"ignorePatterns": ["frontend/", "node_modules/", "desktop-overlay/src/"],
"rules": {
"no-empty": ["error", { "allowEmptyCatch": true }],
"no-unused-vars": ["error", {
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}],
"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|127\\.0\\.0\\.1/]",
"message": "Use internal.headyio.com or api.headyio.com instead of api.headysystems.com"
}]
}
},
"overrides": [
{
"files": [
"tests/**/*.js",
"**/*.test.js",
"**/*.spec.js"
],
"env": {
"jest": true,
"node": true
}
},
{
"files": [
"**/public/**/*.js",
"packages/widget/**/*.js",
"notebooks/**/*.js",
"packages/hc-browser/src/preload.js",
"src/heady-auth-client.js"
],
"env": {
"browser": true,
"es2022": true
}
},
{
"files": [
"packages/heady-music/max-for-live/**/*.js"
],
"rules": {
"no-undef": "off",
"no-unused-vars": "off"
}
}
]
}

13 changes: 9 additions & 4 deletions .github/workflows/fix-gcp-secrets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,26 @@ jobs:
uses: google-github-actions/setup-gcloud@v2

- name: Create Secrets
env:
HEADY_JWT_SECRET: ${{ secrets.HEADY_JWT_SECRET }}
NEON_DATABASE_URL: ${{ secrets.NEON_DATABASE_URL }}
run: |
PROJECT_ID="${{ secrets.GCLOUD_PROJECT_ID }}"
echo "Target Project is $PROJECT_ID"

# Ensure API is enabled
gcloud services enable secretmanager.googleapis.com --project=$PROJECT_ID || true

# Write heady-jwt-secret
echo -n "heady_jwt_phi_1618033988749894" > jwt.txt
# Write heady-jwt-secret (value sourced from GitHub secret HEADY_JWT_SECRET)
printf '%s' "$HEADY_JWT_SECRET" > jwt.txt
gcloud secrets create heady-jwt-secret --data-file=jwt.txt --project=$PROJECT_ID || \
gcloud secrets versions add heady-jwt-secret --data-file=jwt.txt --project=$PROJECT_ID
rm -f jwt.txt

# Write neon-database-url
echo -n "postgresql://neondb_owner:npg_tEA7FfeWb5gZ@ep-cold-snow-aesmiwt9.c-2.us-east-2.aws.neon.tech/neondb?sslmode=require" > db.txt
# Write neon-database-url (value sourced from GitHub secret NEON_DATABASE_URL)
printf '%s' "$NEON_DATABASE_URL" > db.txt
gcloud secrets create neon-database-url --data-file=db.txt --project=$PROJECT_ID || \
gcloud secrets versions add neon-database-url --data-file=db.txt --project=$PROJECT_ID
rm -f db.txt

echo "βœ… Secrets successfully provisioned to $PROJECT_ID"
96 changes: 4 additions & 92 deletions backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,12 @@ const HEADY_RATE_LIMIT_WINDOW_MS = Number(process.env.HEADY_RATE_LIMIT_WINDOW_MS
const HEADY_RATE_LIMIT_MAX = Number(process.env.HEADY_RATE_LIMIT_MAX) || 120;
const HF_MAX_CONCURRENCY = Number(process.env.HF_MAX_CONCURRENCY) || 4;

const HEADY_QA_BACKEND = process.env.HEADY_QA_BACKEND || "auto";
const HEADY_PYTHON_BIN = process.env.HEADY_PYTHON_BIN || "python";
const HEADY_PY_WORKER_TIMEOUT_MS = Number(process.env.HEADY_PY_WORKER_TIMEOUT_MS) || 90_000;
const HEADY_PY_MAX_CONCURRENCY = Number(process.env.HEADY_PY_MAX_CONCURRENCY) || 2;
const HEADY_QA_MAX_NEW_TOKENS = Number(process.env.HEADY_QA_MAX_NEW_TOKENS) || 256;
const HEADY_QA_MODEL = process.env.HEADY_QA_MODEL;
const HEADY_QA_MAX_QUESTION_CHARS = Number(process.env.HEADY_QA_MAX_QUESTION_CHARS) || 4000;
const HEADY_QA_MAX_CONTEXT_CHARS = Number(process.env.HEADY_QA_MAX_CONTEXT_CHARS) || 12000;


const DEFAULT_HF_TEXT_MODEL = process.env.HF_TEXT_MODEL || "gpt2";
const DEFAULT_HF_EMBED_MODEL = process.env.HF_EMBED_MODEL || "sentence-transformers/all-MiniLM-L6-v2";
Expand Down Expand Up @@ -420,13 +418,6 @@ function poolFeatureExtractionOutput(output) {
});
}

function truncateString(value, maxChars) {
if (typeof value !== "string") return "";
if (!Number.isFinite(maxChars) || maxChars <= 0) return value;
if (value.length <= maxChars) return value;
return value.slice(0, maxChars);
}

function createHttpError(status, message, details) {
const err = new Error(message);
err.status = status;
Expand Down Expand Up @@ -643,61 +634,6 @@ function startAdminOperation({ type, script, args, cwd }) {
return op;
}

function computeRiskAnalysis({ question, context }) {
const text = `${question || ""}\n${context || ""}`;

const patterns = [
{ re: /\b(rm\s+-rf|del\s+\/f|format\s+c:|wipe|erase)\b/i, level: "high", title: "Destructive file operations" },
{ re: /\b(drop\s+database|drop\s+table|truncate\s+table)\b/i, level: "high", title: "Destructive database operations" },
{ re: /\b(ssh|private\s+key|api\s+key|password|secret|token)\b/i, level: "medium", title: "Credential or secret handling" },
{ re: /\b(ssn|social\s+security|credit\s+card|passport|driver'?s\s+license)\b/i, level: "high", title: "Potential PII handling" },
{ re: /\b(sql\s+injection|xss|csrf|rce|command\s+injection)\b/i, level: "medium", title: "Security vulnerability context" },
{ re: /\b(powershell|cmd\.exe|bash|shell\s+command|execute\s+command)\b/i, level: "medium", title: "Command execution context" },
];

const items = [];
let maxLevel = "low";
const rank = { low: 0, medium: 1, high: 2 };

for (const p of patterns) {
if (p.re.test(text)) {
items.push({ level: p.level, title: p.title });
if (rank[p.level] > rank[maxLevel]) maxLevel = p.level;
}
}

return {
level: maxLevel,
items,
notes: items.length
? "Risk analysis is heuristic-based. Validate before acting on any destructive or security-sensitive advice."
: "No obvious risk signals detected by heuristics.",
};
}

function buildQaPrompt({ question, context }) {
const safeContext = context ? `Context:\n${context}\n\n` : "";
return (
"You are Heady Systems Q&A. Provide a clear, safe, and concise answer. " +
"Do not reveal secrets, API keys, tokens, or private data.\n\n" +
safeContext +
`Question:\n${question}\n\nAnswer:\n`
);
}

function extractGeneratedText(hfData) {
if (Array.isArray(hfData) && hfData.length > 0 && hfData[0] && typeof hfData[0] === "object") {
if (typeof hfData[0].generated_text === "string") return hfData[0].generated_text;
}
return undefined;
}

function stripPromptEcho(output, prompt) {
if (typeof output !== "string") return output;
if (typeof prompt === "string" && prompt && output.startsWith(prompt)) return output.slice(prompt.length);
return output;
}

async function runPythonQa({ question, context, model, parameters, requestId }) {
const scriptExists = fs.existsSync(PY_WORKER_SCRIPT);
if (!scriptExists) {
Expand Down Expand Up @@ -792,30 +728,6 @@ async function runPythonQa({ question, context, model, parameters, requestId })
);
}

async function runNodeQa({ question, context, model, parameters }) {
const prompt = buildQaPrompt({ question, context });
const usedModel = model || HEADY_QA_MODEL || DEFAULT_HF_TEXT_MODEL;

const mergedParameters = {
max_new_tokens: HEADY_QA_MAX_NEW_TOKENS,
temperature: 0.2,
return_full_text: false,
...(parameters && typeof parameters === "object" ? parameters : {}),
};

const result = await hfInfer({
model: usedModel,
inputs: prompt,
parameters: mergedParameters,
options: { wait_for_model: true },
});

const rawOutput = extractGeneratedText(result.data);
const answer = stripPromptEcho(rawOutput, prompt);

return { ok: true, backend: "node-hf", model: result.model, answer, raw: result.data };
}

const asyncHandler = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);

app.use("/api/admin", requireApiKey);
Expand Down Expand Up @@ -937,7 +849,7 @@ app.post(
if (!HEADY_ADMIN_ENABLE_GPU) {
throw createHttpError(503, "GPU features are disabled");
}
const { inputs, model, parameters } = req.body || {};
const { inputs, model, _parameters } = req.body || {};
if (!inputs) throw createHttpError(400, "inputs is required");
// Stub: echo back with GPU flag; real integration would call remote GPU worker
res.json({
Expand Down Expand Up @@ -1011,7 +923,7 @@ app.post(
app.post(
"/api/admin/test",
asyncHandler(async (req, res) => {
const { root: rootParam, path: relPath, testType } = req.body || {};
const { root: rootParam, path: relPath, _testType } = req.body || {};
const root = assertAdminRoot(rootParam);
const targetPath = resolveAdminPath(root.path, relPath || ".");
const op = startAdminOperation({
Expand Down Expand Up @@ -1399,7 +1311,7 @@ app.get(
}),
);

app.use((err, req, res, next) => {
app.use((err, req, res, _next) => {
const status = typeof err.status === "number" ? err.status : 500;
const payload = {
ok: false,
Expand Down
4 changes: 2 additions & 2 deletions backend/src/controllers/adminController.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const {
HEADY_QA_MODEL,
HEADY_QA_MAX_NEW_TOKENS
} = require("../utils/config");
const { createHttpError, toPosixPath, toRelativePath } = require("../utils/helpers");
const { createHttpError } = require("../utils/helpers");
const { runPythonQa, runPatternScan } = require("../utils/ai");

// Admin Root Management
Expand Down Expand Up @@ -229,7 +229,7 @@ async function inferGpu(req, res) {
if (!HEADY_ADMIN_ENABLE_GPU) {
throw createHttpError(503, "GPU features are disabled");
}
const { inputs, model, parameters } = req.body || {};
const { inputs, model, _parameters } = req.body || {};
if (!inputs) throw createHttpError(400, "inputs is required");
// Stub: echo back with GPU flag; real integration would call remote GPU worker
res.json({
Expand Down
2 changes: 1 addition & 1 deletion backend/src/services/chatService.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ChatService {
this.memory = null; // Placeholder for Vector Store
}

async processMessage({ userId, message, context = {} }) {
async processMessage({ _userId, message, context = {} }) {
// 1. Detect Overwhelm Trigger
const isOverwhelmed = this.detectOverwhelm(message, context.taskCount);

Expand Down
2 changes: 1 addition & 1 deletion backend/src/services/plannerService.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class PlannerService {
};
}

simulate(tasks, iterations) {
simulate(tasks, _iterations) {
// 1. Simple heuristic for the stub
// Real impl would use random sampling from distribution [min, max]

Expand Down
3 changes: 0 additions & 3 deletions backend/src/utils/ai.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const { spawn } = require("child_process");
const {
HF_TOKEN,
DEFAULT_HF_TEXT_MODEL,
DEFAULT_HF_EMBED_MODEL,
HF_MAX_CONCURRENCY,
HEADY_PY_MAX_CONCURRENCY,
HEADY_PYTHON_BIN,
Expand All @@ -28,8 +27,6 @@ const {
} = require("./config");
const { sleep } = require("./helpers");

const fsp = fs.promises;

// Use relative path from src/utils/ai.js to backend/python_worker/process_data.py
const PY_WORKER_SCRIPT = path.join(__dirname, "../../python_worker/process_data.py");

Expand Down
1 change: 0 additions & 1 deletion desktop-overlay/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
// HEADY_BRAND:END

const { app, BrowserWindow } = require('electron')
const path = require('path')

let mainWindow

Expand Down
4 changes: 1 addition & 3 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 All @@ -132,7 +130,7 @@ async function main() {
if (step.tool) {
console.log(` [${stepNum}/${command.steps.length}] πŸ”§ Tool: ${step.tool}`);
if (step.params) console.log(` Params: ${JSON.stringify(step.params)}`);
// In production: await SkillExecutor.executeTool(step.tool, { ...step.params, ...parseArgs(cmdArgs) });
// In production: await SkillExecutor.executeTool(step.tool, { ...step.params });
console.log(` βœ“ Complete`);
} else if (step.swarm) {
console.log(` [${stepNum}/${command.steps.length}] 🐝 Swarm: ${step.swarm}`);
Expand Down
24 changes: 21 additions & 3 deletions heady-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,32 @@ 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 HEADY_CORS_ORIGINS = (process.env.HEADY_CORS_ORIGINS || "")
.split(",")
.map((s) => s.trim())
.filter(Boolean);

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(cors({
origin: (origin, callback) => {
// Allow server-to-server requests with no Origin header
if (!origin) return callback(null, true);
if (HEADY_CORS_ORIGINS.includes("*")) return callback(null, true);
if (HEADY_CORS_ORIGINS.length === 0) {
// No allowlist configured: permit in dev, deny in production
if (process.env.NODE_ENV !== "production") return callback(null, true);
return callback(null, false);
}
if (HEADY_CORS_ORIGINS.includes(origin)) return callback(null, true);
return callback(null, false);
},
methods: ["GET", "POST", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization", "X-Heady-Api-Key"],
maxAge: 600,
}));

function readJsonFileSafe(filePath) {
try {
Expand Down
Loading