summaryrefslogtreecommitdiff
path: root/makima/frontend/src/components
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-03-02 15:18:31 +0000
committerGitHub <noreply@github.com>2026-03-02 15:18:31 +0000
commit78cb861412850889424ae7d5ae5cd952a2b90295 (patch)
tree7a6eb0693457886dbe0eea84c0c1489724791f79 /makima/frontend/src/components
parent2bc1cd4717b587cd2b8ffccd723b62f888e61aa8 (diff)
downloadsoryu-78cb861412850889424ae7d5ae5cd952a2b90295.tar.gz
soryu-78cb861412850889424ae7d5ae5cd952a2b90295.zip
feat: move daemon reauth to daemons page, add contract-backed directive steps, rename Mesh to Exec (#84)
* feat: soryu-co/soryu - makima: Rename Mesh to Exec in navigation * WIP: heartbeat checkpoint * WIP: heartbeat checkpoint * WIP: heartbeat checkpoint * feat: soryu-co/soryu - makima: Add contract-backed steps to directive flow * WIP: heartbeat checkpoint
Diffstat (limited to 'makima/frontend/src/components')
-rw-r--r--makima/frontend/src/components/NavStrip.tsx2
-rw-r--r--makima/frontend/src/components/PhaseConfirmationNotification.tsx2
-rw-r--r--makima/frontend/src/components/SupervisorQuestionNotification.tsx2
-rw-r--r--makima/frontend/src/components/contracts/CommandModePanel.tsx2
-rw-r--r--makima/frontend/src/components/directives/DirectiveDAG.tsx2
-rw-r--r--makima/frontend/src/components/directives/OrchestratorStepNode.tsx2
-rw-r--r--makima/frontend/src/components/directives/StepNode.tsx25
-rw-r--r--makima/frontend/src/components/mesh/TaskOutput.tsx110
8 files changed, 45 insertions, 102 deletions
diff --git a/makima/frontend/src/components/NavStrip.tsx b/makima/frontend/src/components/NavStrip.tsx
index 9556458..17013ac 100644
--- a/makima/frontend/src/components/NavStrip.tsx
+++ b/makima/frontend/src/components/NavStrip.tsx
@@ -14,7 +14,7 @@ const NAV_LINKS: NavLink[] = [
{ label: "Directives", href: "/directives", requiresAuth: true },
{ label: "Orders", href: "/orders", requiresAuth: true },
{ label: "Contracts", href: "/contracts", requiresAuth: true },
- { label: "Mesh", href: "/mesh", requiresAuth: true },
+ { label: "Exec", href: "/exec", requiresAuth: true },
{ label: "Daemons", href: "/daemons", requiresAuth: true },
{ label: "History", href: "/history", requiresAuth: true },
];
diff --git a/makima/frontend/src/components/PhaseConfirmationNotification.tsx b/makima/frontend/src/components/PhaseConfirmationNotification.tsx
index 516211f..2681fdc 100644
--- a/makima/frontend/src/components/PhaseConfirmationNotification.tsx
+++ b/makima/frontend/src/components/PhaseConfirmationNotification.tsx
@@ -86,7 +86,7 @@ export function PhaseConfirmationToast() {
const handleGoToTask = (question: PendingQuestion) => {
dismissNotification(question.questionId);
- navigate(`/mesh/${question.taskId}`);
+ navigate(`/exec/${question.taskId}`);
};
return (
diff --git a/makima/frontend/src/components/SupervisorQuestionNotification.tsx b/makima/frontend/src/components/SupervisorQuestionNotification.tsx
index b1cbacc..e62638c 100644
--- a/makima/frontend/src/components/SupervisorQuestionNotification.tsx
+++ b/makima/frontend/src/components/SupervisorQuestionNotification.tsx
@@ -16,7 +16,7 @@ export function SupervisorQuestionNotification() {
const handleGoToTask = (questionId: string, taskId: string) => {
dismissNotification(questionId);
- navigate(`/mesh/${taskId}`);
+ navigate(`/exec/${taskId}`);
};
return (
diff --git a/makima/frontend/src/components/contracts/CommandModePanel.tsx b/makima/frontend/src/components/contracts/CommandModePanel.tsx
index 832d5ec..b39b309 100644
--- a/makima/frontend/src/components/contracts/CommandModePanel.tsx
+++ b/makima/frontend/src/components/contracts/CommandModePanel.tsx
@@ -65,7 +65,7 @@ export function CommandModePanel({ contract, onUpdate }: CommandModePanelProps)
const handleGoToSupervisor = useCallback(() => {
if (supervisorStatus.supervisorTaskId) {
- navigate(`/mesh/${supervisorStatus.supervisorTaskId}`);
+ navigate(`/exec/${supervisorStatus.supervisorTaskId}`);
}
}, [supervisorStatus.supervisorTaskId, navigate]);
const config = statusConfig[supervisorStatus.status];
diff --git a/makima/frontend/src/components/directives/DirectiveDAG.tsx b/makima/frontend/src/components/directives/DirectiveDAG.tsx
index 142df41..f225356 100644
--- a/makima/frontend/src/components/directives/DirectiveDAG.tsx
+++ b/makima/frontend/src/components/directives/DirectiveDAG.tsx
@@ -146,7 +146,7 @@ function SpecializedStepNode({ step }: { step: SpecializedStep }) {
{step.name}
</span>
<a
- href={`/mesh/${step.taskId}`}
+ href={`/exec/${step.taskId}`}
className="text-[9px] font-mono text-[#556677] hover:text-white underline"
>
View task
diff --git a/makima/frontend/src/components/directives/OrchestratorStepNode.tsx b/makima/frontend/src/components/directives/OrchestratorStepNode.tsx
index 9c8e95e..3fccb4b 100644
--- a/makima/frontend/src/components/directives/OrchestratorStepNode.tsx
+++ b/makima/frontend/src/components/directives/OrchestratorStepNode.tsx
@@ -151,7 +151,7 @@ export function OrchestratorStepNode({
{/* Task link */}
<a
- href={`/mesh/${taskId}`}
+ href={`/exec/${taskId}`}
className={`text-[9px] font-mono text-[#556677] hover:${colors.text} underline block`}
>
{status === "running" ? "View running task" : "View task"}
diff --git a/makima/frontend/src/components/directives/StepNode.tsx b/makima/frontend/src/components/directives/StepNode.tsx
index 2844b4a..775b898 100644
--- a/makima/frontend/src/components/directives/StepNode.tsx
+++ b/makima/frontend/src/components/directives/StepNode.tsx
@@ -28,10 +28,11 @@ interface StepNodeProps {
export function StepNode({ step, onComplete, onFail, onSkip }: StepNodeProps) {
const colors = STATUS_COLORS[step.status] || STATUS_COLORS.pending;
const label = STATUS_LABELS[step.status] || step.status.toUpperCase();
+ const isContractBacked = !!step.contractType;
return (
<div
- className={`${colors.bg} ${colors.border} border rounded px-3 py-2 min-w-[160px] max-w-[220px]`}
+ className={`${colors.bg} ${isContractBacked ? "border-2 border-dashed" : "border"} ${colors.border} rounded px-3 py-2 min-w-[160px] max-w-[220px]`}
>
<div className="flex items-center justify-between gap-2 mb-1">
<span className="text-[11px] font-mono text-white truncate font-medium">
@@ -41,14 +42,32 @@ export function StepNode({ step, onComplete, onFail, onSkip }: StepNodeProps) {
{label}
</span>
</div>
+ {isContractBacked && (
+ <div className="flex items-center gap-1 mb-1">
+ <span className="text-[8px] font-mono text-violet-400 bg-violet-400/10 border border-violet-400/20 rounded px-1 py-0.5 uppercase tracking-wide">
+ CONTRACT
+ </span>
+ <span className="text-[8px] font-mono text-violet-300/60">
+ {step.contractType}
+ </span>
+ </div>
+ )}
{step.description && (
<p className="text-[10px] text-[#7788aa] font-mono truncate mb-1">
{step.description}
</p>
)}
- {step.taskId && (
+ {step.contractId && (
+ <a
+ href={`/contracts/${step.contractId}`}
+ className="text-[9px] font-mono text-violet-400/60 hover:text-violet-300 underline block mb-1"
+ >
+ {step.status === "running" ? "Contract running..." : "View contract"}
+ </a>
+ )}
+ {step.taskId && !step.contractId && (
<a
- href={`/mesh/${step.taskId}`}
+ href={`/exec/${step.taskId}`}
className="text-[9px] font-mono text-[#556677] hover:text-[#75aafc] underline block mb-1"
>
{step.status === "running" ? "Auto-executing..." : "View task"}
diff --git a/makima/frontend/src/components/mesh/TaskOutput.tsx b/makima/frontend/src/components/mesh/TaskOutput.tsx
index 2db4250..1a376ad 100644
--- a/makima/frontend/src/components/mesh/TaskOutput.tsx
+++ b/makima/frontend/src/components/mesh/TaskOutput.tsx
@@ -309,7 +309,21 @@ function OutputEntryRenderer({ entry, pendingQuestionIds, onAnswerQuestion }: Ou
);
case "auth_required":
- return <AuthRequiredEntry entry={entry} />;
+ return (
+ <div className="bg-amber-900/30 border border-amber-500/50 rounded p-3 my-2">
+ <div className="flex items-center gap-2 text-amber-400 font-semibold mb-1">
+ <span>&#x26A0;</span>
+ <span>Authentication Expired{entry.toolInput?.hostname ? ` (${entry.toolInput.hostname})` : ""}</span>
+ </div>
+ <p className="text-amber-200/80 text-sm">
+ The daemon's OAuth token has expired. Go to the{" "}
+ <a href="/daemons" className="text-[#75aafc] hover:text-[#9bc3ff] underline">
+ Daemons page
+ </a>{" "}
+ to reauthorize, then retry this task.
+ </p>
+ </div>
+ );
case "supervisor_question":
return (
@@ -501,98 +515,8 @@ function SupervisorQuestionEntry({
);
}
-function AuthRequiredEntry({ entry }: { entry: TaskOutputEvent }) {
- const [authCode, setAuthCode] = useState("");
- const [submitting, setSubmitting] = useState(false);
- const [submitted, setSubmitted] = useState(false);
- const [error, setError] = useState<string | null>(null);
-
- const loginUrl = entry.toolInput?.loginUrl as string | undefined;
- const hostname = entry.toolInput?.hostname as string | undefined;
- // Get taskId from entry or fallback to toolInput (for robustness)
- const taskId = entry.taskId || (entry.toolInput?.taskId as string | undefined);
-
- const handleSubmit = async (e: React.FormEvent) => {
- e.preventDefault();
- if (!authCode.trim() || !taskId) return;
-
- setSubmitting(true);
- setError(null);
-
- try {
- // Send the auth code to the task via the message endpoint
- await sendTaskMessage(taskId, `AUTH_CODE:${authCode.trim()}`);
- setSubmitted(true);
- } catch (err) {
- setError(err instanceof Error ? err.message : "Failed to submit code");
- } finally {
- setSubmitting(false);
- }
- };
-
- if (submitted) {
- return (
- <div className="bg-green-900/30 border border-green-500/50 rounded p-3 my-2">
- <div className="flex items-center gap-2 text-green-400 font-semibold">
- <span>✓</span>
- <span>Authentication code submitted</span>
- </div>
- <p className="text-green-200/80 text-sm mt-1">
- Waiting for authentication to complete...
- </p>
- </div>
- );
- }
-
- return (
- <div className="bg-amber-900/30 border border-amber-500/50 rounded p-3 my-2">
- <div className="flex items-center gap-2 text-amber-400 font-semibold mb-2">
- <span>🔐</span>
- <span>Authentication Required{hostname ? ` (${hostname})` : ""}</span>
- </div>
- <p className="text-amber-200/80 text-sm mb-3">
- The daemon's OAuth token has expired. Click the button to login, then paste the code below:
- </p>
-
- <div className="flex flex-col gap-3">
- {loginUrl ? (
- <a
- href={loginUrl}
- target="_blank"
- rel="noopener noreferrer"
- className="inline-block bg-amber-500 hover:bg-amber-400 text-black font-medium px-4 py-2 rounded transition-colors text-center"
- >
- 1. Login to Claude
- </a>
- ) : (
- <p className="text-red-400 text-sm">Login URL not available</p>
- )}
-
- <form onSubmit={handleSubmit} className="flex gap-2">
- <input
- type="text"
- value={authCode}
- onChange={(e) => setAuthCode(e.target.value)}
- placeholder="2. Paste authentication code here"
- className="flex-1 bg-[#0a1525] border border-amber-500/30 rounded px-3 py-2 text-amber-100 placeholder-amber-500/50 focus:outline-none focus:border-amber-400"
- disabled={submitting}
- />
- <button
- type="submit"
- disabled={submitting || !authCode.trim()}
- className="bg-amber-500 hover:bg-amber-400 disabled:bg-amber-700 disabled:cursor-not-allowed text-black font-medium px-4 py-2 rounded transition-colors"
- >
- {submitting ? "..." : "Submit"}
- </button>
- </form>
-
- {error && (
- <p className="text-red-400 text-sm">{error}</p>
- )}
- </div>
- </div>
- );
-}
+// AuthRequiredEntry has been removed - auth flow is now on the Daemons page.
+// The "auth_required" case in OutputEntryRenderer shows a redirect message instead.
/** Entry for phase transition confirmations */
function PhaseConfirmationEntry({