diff options
Diffstat (limited to 'makima/frontend/src/components/mesh/TaskOutput.tsx')
| -rw-r--r-- | makima/frontend/src/components/mesh/TaskOutput.tsx | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/makima/frontend/src/components/mesh/TaskOutput.tsx b/makima/frontend/src/components/mesh/TaskOutput.tsx index 10de225..cb0eba3 100644 --- a/makima/frontend/src/components/mesh/TaskOutput.tsx +++ b/makima/frontend/src/components/mesh/TaskOutput.tsx @@ -275,7 +275,103 @@ function OutputEntryRenderer({ entry }: { entry: TaskOutputEvent }) { </div> ); + case "auth_required": + return <AuthRequiredEntry entry={entry} />; + default: return null; } } + +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> + ); +} |
