UNPKG

@blundergoat/goat-flow

Version:

AI coding agent harness and local dashboard for Claude Code, OpenAI Codex, Google Antigravity, and GitHub Copilot - setup audits, guardrails, structured skills, deny hooks, and persistent learning loops.

64 lines (63 loc) 3.68 kB
"use strict"; /** * Browser-side Alpine.js data model for the GOAT Flow dashboard. * This stays as a classic script because the dashboard shell loads it with a * plain `<script>` tag rather than an ES module import. */ /** Encode dropped terminal image files into the JSON payload expected by the upload route. */ async function encodeTerminalUploadFiles(files) { return Promise.all(files.map(async (file) => ({ name: file.name, data: await dashboardFileToBase64(file), }))); } /** Decode the terminal upload response fields used for paste notes and toasts. */ function readTerminalUploadResult(payload) { return { note: typeof payload.note === "string" ? payload.note : "", accepted: Array.isArray(payload.accepted) ? payload.accepted : [], rejected: Array.isArray(payload.rejected) ? payload.rejected : [], }; } /** Decode one rejected upload entry into the user-facing file name and reason. */ function readRejectedTerminalUpload(entry) { const record = isRecord(entry) ? entry : {}; return { name: typeof record["originalName"] === "string" ? record["originalName"] : "file", reason: typeof record["reason"] === "string" ? record["reason"] : "unknown reason", }; } /** Paste terminal upload notes and surface accepted/rejected file feedback. */ function showTerminalUploadResult(ctx, sessionId, result) { if (result.note.length > 0) { dashboardSendToTerminalSession(ctx, sessionId, result.note, { adapt: false, }); } if (result.rejected.length > 0) { for (const entry of result.rejected) { const rejected = readRejectedTerminalUpload(entry); ctx.showToast(`Rejected ${rejected.name}: ${rejected.reason}`, true); } return; } if (result.accepted.length > 0) { ctx.showToast(`Attached ${result.accepted.length} image${result.accepted.length === 1 ? "" : "s"}`, false); } } /** * Alpine.js data factory for the dashboard shell. * Fragments merge into one classic-script object because Alpine binds methods by * property name from server-rendered HTML. The descriptor-preserving merge keeps * getters/setters intact while letting large view clusters live in smaller files. */ function app() { const supportedAgents = readInjectedSupportedAgents(); const defaultRunner = supportedAgents[0]?.id ?? "claude"; const defaultSetupAgents = buildDefaultSetupAgents(supportedAgents, defaultRunner); return dashboardMergeAppFragments(dashboardCoreStateFragment(supportedAgents, defaultRunner), dashboardActiveTerminalSessionFragment(), dashboardTerminalStatusAccessorsFragment(), dashboardWorkspaceCollectionsStateFragment(), dashboardQualitySetupStateFragment(supportedAgents, defaultRunner, defaultSetupAgents), dashboardPromptBrowserStateFragment(), dashboardCustomPromptValidationFragment(), dashboardCustomPromptEditorActionsFragment(), dashboardQualityPromptActionsFragment(), dashboardTerminalImageUploadFragment(), dashboardAuditAndNavigationActionsFragment(), dashboardAgentPlanHookLoadersFragment(supportedAgents), dashboardHookSetupActionsFragment(supportedAgents), dashboardSetupQualityLoadersFragment(), dashboardSkillQualityInventoryLoadersFragment(), dashboardSkillQualityReportFragment(), dashboardSkillEvaluatorResultFragment(), dashboardSkillEvaluatorClipboardFragment(), dashboardSkillEvaluatorInputFragment(), dashboardProjectActionsFragment(), dashboardUtilityActionsFragment(), dashboardTerminalLaunchActionsFragment(), dashboardTerminalSessionActionsFragment(), dashboardTimeFormattingFragment()); }