@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
JavaScript
;
/**
* 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());
}