From 8306f92fd725a58b433db354672dac40addcb020 Mon Sep 17 00:00:00 2001
From: SomeRandmGuyy <127457986+SomeRandmGuyy@users.noreply.github.com>
Date: Wed, 16 Sep 2026 02:22:59 +0000
Subject: [PATCH 1/2] fix: unstick /autoclip runs and Settings key entry
- Agent tab: keep polling the run detail in hidden tabs; each poll ticks the
background media-fetch job, and React Query pauses interval refetches by
default, so a run left in another tab never advanced.
- media-fetch-job: a run parked as MEDIA_FETCH with no job state now fails
visibly on the first tick instead of returning idle forever.
- Settings: Higgsfield status read through its own loader (the generic
settings reader treated *_API_KEY/*_SECRET as encrypted and returned null,
so a saved key showed Not configured); Crayo Test no longer maps to the
app's 'Please sign in' message; Test is disabled while a card has unsaved
text since it only ever checks the stored key.
Co-Authored-By: Claude
---
.../settings/integrations-panel.tsx | 104 ++++---
src/lib/server/integrations.ts | 27 +-
src/lib/server/media-fetch-job.server.ts | 286 +++++++++++++++---
src/routes/_app/agent.tsx | 80 +++--
4 files changed, 368 insertions(+), 129 deletions(-)
diff --git a/src/components/settings/integrations-panel.tsx b/src/components/settings/integrations-panel.tsx
index f97c181..855c326 100644
--- a/src/components/settings/integrations-panel.tsx
+++ b/src/components/settings/integrations-panel.tsx
@@ -49,12 +49,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ErrorState } from "@/components/ui/error-state";
import { Skeleton } from "@/components/ui/skeleton";
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogTitle,
-} from "@/components/ui/dialog";
+import { Dialog, DialogContent, DialogDescription, DialogTitle } from "@/components/ui/dialog";
import { PUBLISHERS_QUERY_KEY } from "@/lib/publishers";
import { SOCIAL_QUERY_KEY } from "@/lib/social";
function XMarkIcon({ className }: { className?: string }) {
@@ -128,19 +123,19 @@ export function IntegrationsPanel() {
Add-ons
- ClippyOS is an OS. These integrations are add-ons. Core AI is required. Daytona is
- the browser runtime for Social Computer Use. Keys stay on the server.
+ ClippyOS is an OS. These integrations are add-ons. Core AI is required. Daytona is the
+ browser runtime for Social Computer Use. Keys stay on the server.
{query.data.role === "member" && inheritWorkspaceApis ? (
- You’re using the owner’s workspace APIs. Ask an owner to turn that off if you
- need to connect your own keys.
+ You’re using the owner’s workspace APIs. Ask an owner to turn that off if you need to
+ connect your own keys.
) : null}
{query.data.role === "member" && !inheritWorkspaceApis ? (
- These keys are yours. They don’t use the owner’s APIs unless an owner shares
- workspace APIs with this login.
+ These keys are yours. They don’t use the owner’s APIs unless an owner shares workspace
+ APIs with this login.
) : null}
@@ -150,11 +145,7 @@ export function IntegrationsPanel() {
key={id}
id={id}
isAdmin={Boolean(isAdmin)}
- canEdit={
- id === "whop"
- ? Boolean(isAdmin)
- : canEditIntegrations
- }
+ canEdit={id === "whop" ? Boolean(isAdmin) : canEditIntegrations}
last4={query.data.items[id].last4}
health={query.data.items[id].health}
lastTestedAt={query.data.items[id].lastTestedAt}
@@ -173,7 +164,8 @@ export function IntegrationsPanel() {
ClippyOS MCP
- Remote MCP URL plus scoped connector tokens for Grok Bot and Cursor. Publish still honors Approvals.
+ Remote MCP URL plus scoped connector tokens for Grok Bot and Cursor. Publish still
+ honors Approvals.
@@ -215,12 +207,20 @@ function IntegrationCard({
const [pendingDisconnect, setPendingDisconnect] = useState(false);
const [fields, setFields] = useState>({});
const configured = health !== "not_configured";
+ // Test always checks the credential already stored on the server, never the value
+ // typed above — Save first, or a correct key you just pasted comes back "rejected".
+ const hasUnsavedFields = Object.values(fields).some((value) => value.trim().length > 0);
useEffect(() => {
if (id !== "x") return;
function onMessage(event: MessageEvent) {
if (event.origin !== window.location.origin) return;
- const data = event.data as { source?: string; ok?: boolean; provider?: string; error?: string };
+ const data = event.data as {
+ source?: string;
+ ok?: boolean;
+ provider?: string;
+ error?: string;
+ };
if (!data || data.source !== "clippy-social-oauth") return;
if (data.provider && data.provider !== "x") return;
void queryClient.invalidateQueries({ queryKey: INTEGRATIONS_QUERY_KEY });
@@ -345,13 +345,9 @@ function IntegrationCard({
{copy.required ? Required : null}
{ADDON_META[id].requiredFor}
-
- Used by: {ADDON_META[id].usedBy.join(" · ")}
-
+ Used by: {ADDON_META[id].usedBy.join(" · ")}
- {lastTestedAt
- ? `Last tested ${formatRelativeTime(lastTestedAt)}`
- : "Not tested yet"}
+ {lastTestedAt ? `Last tested ${formatRelativeTime(lastTestedAt)}` : "Not tested yet"}
{lastError ? ` · ${lastError}` : null}
{id === "x" ? (
@@ -377,7 +373,11 @@ function IntegrationCard({
) : null}
- {id === "ai" && canEdit ?
: null}
+ {id === "ai" && canEdit ? (
+
+
+
+ ) : null}
{canEdit ? (