Fix: integration mcp is not loading

This commit is contained in:
Harshith Mullapudi 2025-07-24 10:35:47 +05:30
parent 8ec974f942
commit a66a472112
30 changed files with 645 additions and 311 deletions

View File

@ -5,6 +5,7 @@ import {
useCallback,
useImperativeHandle,
forwardRef,
useState,
} from "react";
import Sigma from "sigma";
import GraphologyGraph from "graphology";
@ -186,10 +187,6 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
}
groups[key].relations.push(triplet.relation.value);
groups[key].relationData.push(triplet.relation);
groups[key].label = groups[key].relations
.join(", ")
.replace("HAS_", "")
.toLowerCase();
return groups;
},
@ -216,7 +213,7 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
});
graph.forEachEdge((edge) => {
graph.setEdgeAttribute(edge, "highlighted", false);
graph.setEdgeAttribute(edge, "color", theme.link.stroke);
graph.setEdgeAttribute(edge, "color", "#0000001A");
});
selectedNodeRef.current = null;
selectedEdgeRef.current = null;
@ -271,6 +268,71 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
useImperativeHandle(ref, () => graphRefMethods.current);
// Calculate optimal ForceAtlas2 parameters based on graph properties
const calculateOptimalParameters = useCallback((graph: GraphologyGraph) => {
const nodeCount = graph.order;
const edgeCount = graph.size;
if (nodeCount === 0)
return { scalingRatio: 30, gravity: 5, iterations: 600 };
// Calculate graph density (0 to 1)
const maxPossibleEdges = (nodeCount * (nodeCount - 1)) / 2;
const density = maxPossibleEdges > 0 ? edgeCount / maxPossibleEdges : 0;
// Calculate optimal scaling ratio based on node count
// More nodes = need more space to prevent overcrowding
let scalingRatio: number;
if (nodeCount < 10) {
scalingRatio = 15; // Tight for small graphs
} else if (nodeCount < 50) {
scalingRatio = 20 + (nodeCount - 10) * 0.5; // Gradual increase
} else if (nodeCount < 200) {
scalingRatio = 40 + (nodeCount - 50) * 0.2; // Slower increase
} else {
scalingRatio = Math.min(80, 70 + (nodeCount - 200) * 0.05); // Cap at 80
}
// Calculate optimal gravity based on density and node count
let gravity: number;
if (density > 0.3) {
// Dense graphs need less gravity to prevent overcrowding
gravity = 1 + density * 2;
} else if (density > 0.1) {
// Medium density graphs
gravity = 3 + density * 5;
} else {
// Sparse graphs need more gravity to keep components together
gravity = Math.min(8, 5 + (1 - density) * 3);
}
// Adjust gravity based on node count
if (nodeCount < 20) {
gravity *= 1.5; // Smaller graphs benefit from stronger gravity
} else if (nodeCount > 100) {
gravity *= 0.8; // Larger graphs need gentler gravity
}
// Calculate iterations based on complexity
const complexity = nodeCount + edgeCount;
let iterations: number;
if (complexity < 50) {
iterations = 400;
} else if (complexity < 200) {
iterations = 600;
} else if (complexity < 500) {
iterations = 800;
} else {
iterations = Math.min(1200, 1000 + complexity * 0.2);
}
return {
scalingRatio: Math.round(scalingRatio * 10) / 10,
gravity: Math.round(gravity * 10) / 10,
iterations: Math.round(iterations),
};
}, []);
useEffect(() => {
if (isInitializedRef.current || !containerRef.current) return;
isInitializedRef.current = true;
@ -303,25 +365,28 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
// });
// layout.start();
// Calculate optimal parameters for this graph
const optimalParams = calculateOptimalParameters(graph);
const settings = forceAtlas2.inferSettings(graph);
forceAtlas2.assign(graph, {
iterations: 600,
iterations: optimalParams.iterations,
settings: {
...settings,
barnesHutOptimize: true,
strongGravityMode: false,
gravity: 1,
scalingRatio: 10,
slowDown: 5,
strongGravityMode: true,
gravity: optimalParams.gravity,
scalingRatio: optimalParams.scalingRatio,
slowDown: 3,
},
});
noverlap.assign(graph, {
maxIterations: 150,
maxIterations: 200,
settings: {
margin: 5,
expansion: 1.1,
gridSize: 20,
margin: 10,
expansion: 1.5,
gridSize: 30,
},
});
}

View File

@ -1,12 +1,14 @@
import React, { useCallback, useState } from "react";
import { useFetcher } from "@remix-run/react";
import { Button } from "~/components/ui/button";
import { Check } from "lucide-react";
import { Check, Copy } from "lucide-react";
import { Input } from "../ui/input";
interface MCPAuthSectionProps {
integration: {
id: string;
name: string;
slug: string;
};
activeAccount?: {
id: string;
@ -17,6 +19,49 @@ interface MCPAuthSectionProps {
hasMCPAuth: boolean;
}
interface MCPUrlBoxProps {
mcpUrl: string;
}
function MCPUrlBox({ mcpUrl }: MCPUrlBoxProps) {
const [copied, setCopied] = useState(false);
const handleCopy = useCallback(() => {
navigator.clipboard.writeText(mcpUrl).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
});
}, [mcpUrl]);
return (
<div className="mb-3 flex items-center gap-2">
<Input
type="text"
value={mcpUrl}
readOnly
className="w-full rounded px-2 py-1 font-mono text-sm"
style={{ maxWidth: 400 }}
onFocus={(e) => e.target.select()}
/>
<Button
type="button"
variant={copied ? "secondary" : "ghost"}
onClick={handleCopy}
aria-label={copied ? "Copied" : "Copy MCP URL"}
disabled={copied}
>
{copied ? (
<span className="flex items-center gap-1">
<Check size={16} /> Copied
</span>
) : (
<Copy size={16} />
)}
</Button>
</div>
);
}
export function MCPAuthSection({
integration,
activeAccount,
@ -26,7 +71,10 @@ export function MCPAuthSection({
const mcpFetcher = useFetcher<{ redirectURL: string }>();
const disconnectMcpFetcher = useFetcher();
const isMCPConnected = activeAccount?.integrationConfiguration?.mcp;
const isMCPConnected = !!activeAccount?.integrationConfiguration?.mcp;
const isConnected = !!activeAccount;
const mcpUrl = `https://core.heysol.ai/api/v1/mcp/${integration.slug}`;
const handleMCPConnect = useCallback(() => {
setIsMCPConnecting(true);
@ -43,7 +91,7 @@ export function MCPAuthSection({
encType: "application/json",
},
);
}, [integration.id, mcpFetcher]);
}, [integration.id, mcpFetcher, activeAccount?.id]);
const handleMCPDisconnect = useCallback(() => {
if (!activeAccount?.id) return;
@ -77,57 +125,78 @@ export function MCPAuthSection({
}
}, [disconnectMcpFetcher.state, disconnectMcpFetcher.data]);
if (!hasMCPAuth || !activeAccount) return null;
// Show nothing if not connected at all
if (!isConnected) return null;
// Show MCP box if:
// - hasMCPAuth is true (always show MCP section)
// - OR hasMCPAuth is false but integration is connected (show MCP URL box only)
return (
<div className="mt-6 space-y-2">
<h3 className="text-lg font-medium">MCP Authentication</h3>
{isMCPConnected ? (
<div className="bg-background-3 rounded-lg p-4">
<div className="text-sm">
<p className="inline-flex items-center gap-2 font-medium">
<Check size={16} /> MCP Connected
</p>
<p className="text-muted-foreground mb-3">
MCP (Model Context Protocol) authentication is active
{hasMCPAuth ? (
isMCPConnected ? (
<div className="bg-background-3 rounded-lg p-4">
<div className="text-sm">
<p className="inline-flex items-center gap-2 font-medium">
<Check size={16} /> MCP Connected
</p>
<p className="text-muted-foreground mb-3">
MCP (Model Context Protocol) authentication is active
</p>
<MCPUrlBox mcpUrl={mcpUrl} />
<div className="flex w-full justify-end">
<Button
variant="destructive"
disabled={disconnectMcpFetcher.state === "submitting"}
onClick={handleMCPDisconnect}
>
{disconnectMcpFetcher.state === "submitting"
? "Disconnecting..."
: "Disconnect"}
</Button>
</div>
</div>
</div>
) : (
<div className="bg-background-3 rounded-lg p-4">
<h4 className="text-md mb-1 font-medium">
MCP (Model Context Protocol) Authentication
</h4>
<p className="text-muted-foreground mb-3 text-sm">
This integration requires MCP (Model Context Protocol)
authentication. Please provide the required MCP credentials in
addition to any other authentication method.
</p>
<div className="flex w-full justify-end">
<Button
variant="destructive"
disabled={disconnectMcpFetcher.state === "submitting"}
onClick={handleMCPDisconnect}
variant="secondary"
disabled={isMCPConnecting || mcpFetcher.state === "submitting"}
onClick={handleMCPConnect}
>
{disconnectMcpFetcher.state === "submitting"
? "Disconnecting..."
: "Disconnect"}
{isMCPConnecting || mcpFetcher.state === "submitting"
? "Connecting..."
: `Connect for MCP`}
</Button>
</div>
</div>
</div>
) : activeAccount ? (
)
) : (
// hasMCPAuth is false, but integration is connected: show just the MCPUrlBox
<div className="bg-background-3 rounded-lg p-4">
<h4 className="text-md mb-1 font-medium">
MCP (Model Context Protocol) Authentication
</h4>
<p className="text-muted-foreground mb-3 text-sm">
This integration requires MCP (Model Context Protocol)
authentication. Please provide the required MCP credentials in
addition to any other authentication method.
</p>
<div className="flex w-full justify-end">
<Button
variant="secondary"
disabled={isMCPConnecting || mcpFetcher.state === "submitting"}
onClick={handleMCPConnect}
>
{isMCPConnecting || mcpFetcher.state === "submitting"
? "Connecting..."
: `Connect for MCP`}
</Button>
<div className="text-sm">
<p className="inline-flex items-center gap-2 font-medium">
<Check size={16} /> Integration Connected
</p>
<p className="text-muted-foreground mb-3">
You can use the MCP endpoint for this integration:
</p>
<MCPUrlBox mcpUrl={mcpUrl} />
</div>
</div>
) : null}
)}
</div>
);
}

View File

@ -14,7 +14,7 @@ export function Section({
children,
}: SectionProps) {
return (
<div className="flex h-full gap-6">
<div className="flex h-full w-full gap-6">
<div className="flex w-[400px] shrink-0 flex-col">
{icon && <>{icon}</>}
<h3 className="text-lg"> {title} </h3>

View File

@ -1,16 +1,16 @@
<svg width="282" height="282" viewBox="0 0 282 282" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M80.0827 34.974C92.7457 3.8081 120.792 17.7546 134.476 29.5676C135.325 30.301 135.792 31.3761 135.792 32.4985V250.806C135.792 251.98 135.258 253.117 134.349 253.858C103.339 279.155 85.2835 259.158 80.0827 245.771C44.9187 241.11 43.965 209.932 47.8837 194.925C15.173 187.722 17.5591 152.731 22.841 136.135C9.34813 107.747 33.9141 90.4097 47.8837 85.2899C40.524 50.5456 66.2831 37.2692 80.0827 34.974Z" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(0.591888 0.80602 -0.783494 0.6214 77.3574 37.2551)" stroke="#C15E50" stroke-width="5"/>
<path d="M49.1309 84.7972L136.212 176.505" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(0.814972 0.5795 -0.549892 0.835235 32.5566 143.53)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(0.686415 -0.72721 0.700262 0.713886 35.4785 139.482)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(0.528012 -0.849237 0.830187 0.557485 49.1133 196.162)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(-0.979793 0.200014 -0.185721 -0.982602 135.791 117.215)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(0.532065 -0.846704 0.827428 0.561572 81.252 246.769)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(-0.503861 -0.863784 0.846088 -0.533044 137.443 252.884)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(-0.0852204 0.996362 -0.99576 -0.0919863 110.471 150.615)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(0.998935 -0.0461398 0.0427267 0.999087 49.1133 198.186)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(-0.165686 0.986178 -0.983932 -0.178541 100.73 66.6073)" stroke="#C15E50" stroke-width="5"/>
<path d="M80.0827 34.974C92.7457 3.8081 120.792 17.7546 134.476 29.5676C135.325 30.301 135.792 31.3761 135.792 32.4985V250.806C135.792 251.98 135.258 253.117 134.349 253.858C103.339 279.155 85.2835 259.158 80.0827 245.771C44.9187 241.11 43.965 209.932 47.8837 194.925C15.173 187.722 17.5591 152.731 22.841 136.135C9.34813 107.747 33.9141 90.4097 47.8837 85.2899C40.524 50.5456 66.2831 37.2692 80.0827 34.974Z" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(0.591888 0.80602 -0.783494 0.6214 77.3574 37.2551)" stroke="#C15E50" strokeWidth="5"/>
<path d="M49.1309 84.7972L136.212 176.505" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(0.814972 0.5795 -0.549892 0.835235 32.5566 143.53)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(0.686415 -0.72721 0.700262 0.713886 35.4785 139.482)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(0.528012 -0.849237 0.830187 0.557485 49.1133 196.162)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(-0.979793 0.200014 -0.185721 -0.982602 135.791 117.215)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(0.532065 -0.846704 0.827428 0.561572 81.252 246.769)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(-0.503861 -0.863784 0.846088 -0.533044 137.443 252.884)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(-0.0852204 0.996362 -0.99576 -0.0919863 110.471 150.615)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(0.998935 -0.0461398 0.0427267 0.999087 49.1133 198.186)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(-0.165686 0.986178 -0.983932 -0.178541 100.73 66.6073)" stroke="#C15E50" strokeWidth="5"/>
<circle cx="103.955" cy="66.3968" r="11.0444" fill="#C15E50"/>
<circle cx="90.4996" cy="129.403" r="11.5465" fill="#C15E50"/>
<circle cx="103.955" cy="197.449" r="11.0444" fill="#C15E50"/>
@ -19,18 +19,18 @@
<circle cx="29.0525" cy="140.996" r="13.0525" fill="#C15E50"/>
<circle cx="79.0009" cy="246.875" r="7.02828" fill="#C15E50"/>
<path d="M53.0314 195.433C53.0314 199.869 49.4352 203.466 44.9991 203.466C40.563 203.466 36.9668 199.869 36.9668 195.433C36.9668 190.997 40.563 187.401 44.9991 187.401C49.4352 187.401 53.0314 190.997 53.0314 195.433Z" fill="#C15E50"/>
<path d="M202.806 247.026C190.143 278.192 162.097 264.245 148.413 252.432C147.563 251.699 147.097 250.624 147.097 249.501V31.1935C147.097 30.0203 147.631 28.8833 148.54 28.1417C179.549 2.84476 197.605 22.8418 202.806 36.2294C237.97 40.8903 238.924 72.0684 235.005 87.0748C267.716 94.2779 265.33 129.269 260.048 145.865C273.541 174.253 248.975 191.59 235.005 196.71C242.365 231.454 216.606 244.731 202.806 247.026Z" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(-0.591888 -0.80602 0.783494 -0.6214 205.531 244.745)" stroke="#C15E50" stroke-width="5"/>
<path d="M233.758 197.203L146.677 105.495" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(-0.814972 -0.5795 0.549892 -0.835235 250.332 138.47)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(-0.686415 0.72721 -0.700262 -0.713886 247.41 142.518)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(-0.528012 0.849237 -0.830187 -0.557485 233.775 85.838)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(0.979793 -0.200014 0.185721 0.982602 147.098 164.785)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(-0.532065 0.846704 -0.827428 -0.561572 201.637 35.2307)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(0.503861 0.863784 -0.846088 0.533044 145.445 29.1161)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(0.0852204 -0.996362 0.99576 0.0919863 172.418 131.385)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(-0.998935 0.0461398 -0.0427267 -0.999087 233.775 83.8137)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(0.165686 -0.986178 0.983932 0.178541 182.158 215.393)" stroke="#C15E50" stroke-width="5"/>
<path d="M202.806 247.026C190.143 278.192 162.097 264.245 148.413 252.432C147.563 251.699 147.097 250.624 147.097 249.501V31.1935C147.097 30.0203 147.631 28.8833 148.54 28.1417C179.549 2.84476 197.605 22.8418 202.806 36.2294C237.97 40.8903 238.924 72.0684 235.005 87.0748C267.716 94.2779 265.33 129.269 260.048 145.865C273.541 174.253 248.975 191.59 235.005 196.71C242.365 231.454 216.606 244.731 202.806 247.026Z" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(-0.591888 -0.80602 0.783494 -0.6214 205.531 244.745)" stroke="#C15E50" strokeWidth="5"/>
<path d="M233.758 197.203L146.677 105.495" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(-0.814972 -0.5795 0.549892 -0.835235 250.332 138.47)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(-0.686415 0.72721 -0.700262 -0.713886 247.41 142.518)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(-0.528012 0.849237 -0.830187 -0.557485 233.775 85.838)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(0.979793 -0.200014 0.185721 0.982602 147.098 164.785)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(-0.532065 0.846704 -0.827428 -0.561572 201.637 35.2307)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(0.503861 0.863784 -0.846088 0.533044 145.445 29.1161)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(0.0852204 -0.996362 0.99576 0.0919863 172.418 131.385)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(-0.998935 0.0461398 -0.0427267 -0.999087 233.775 83.8137)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(0.165686 -0.986178 0.983932 0.178541 182.158 215.393)" stroke="#C15E50" strokeWidth="5"/>
<circle cx="178.934" cy="215.603" r="11.0444" transform="rotate(180 178.934 215.603)" fill="#C15E50"/>
<circle cx="192.389" cy="152.597" r="11.5465" transform="rotate(180 192.389 152.597)" fill="#C15E50"/>
<circle cx="178.934" cy="84.5506" r="11.0444" transform="rotate(180 178.934 84.5506)" fill="#C15E50"/>

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -15,7 +15,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
<path
d="M80.0827 34.974C92.7457 3.8081 120.792 17.7546 134.476 29.5676C135.325 30.301 135.792 31.3761 135.792 32.4985V250.806C135.792 251.98 135.258 253.117 134.349 253.858C103.339 279.155 85.2835 259.158 80.0827 245.771C44.9187 241.11 43.965 209.932 47.8837 194.925C15.173 187.722 17.5591 152.731 22.841 136.135C9.34813 107.747 33.9141 90.4097 47.8837 85.2899C40.524 50.5456 66.2831 37.2692 80.0827 34.974Z"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -23,12 +23,12 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.591888 0.80602 -0.783494 0.6214 77.3574 37.2551)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<path
d="M49.1309 84.7972L136.212 176.505"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -36,7 +36,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.814972 0.5795 -0.549892 0.835235 32.5566 143.53)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -44,7 +44,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.686415 -0.72721 0.700262 0.713886 35.4785 139.482)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -52,7 +52,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.528012 -0.849237 0.830187 0.557485 49.1133 196.162)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -60,7 +60,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.979793 0.200014 -0.185721 -0.982602 135.791 117.215)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -68,7 +68,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.532065 -0.846704 0.827428 0.561572 81.252 246.769)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -76,7 +76,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.503861 -0.863784 0.846088 -0.533044 137.443 252.884)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -84,7 +84,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.0852204 0.996362 -0.99576 -0.0919863 110.471 150.615)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -92,7 +92,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.998935 -0.0461398 0.0427267 0.999087 49.1133 198.186)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -100,7 +100,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.165686 0.986178 -0.983932 -0.178541 100.73 66.6073)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<circle cx="103.955" cy="66.3968" r="11.0444" fill="#C15E50" />
<circle cx="90.4996" cy="129.403" r="11.5465" fill="#C15E50" />
@ -119,7 +119,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
<path
d="M202.806 247.026C190.143 278.192 162.097 264.245 148.413 252.432C147.563 251.699 147.097 250.624 147.097 249.501V31.1935C147.097 30.0203 147.631 28.8833 148.54 28.1417C179.549 2.84476 197.605 22.8418 202.806 36.2294C237.97 40.8903 238.924 72.0684 235.005 87.0748C267.716 94.2779 265.33 129.269 260.048 145.865C273.541 174.253 248.975 191.59 235.005 196.71C242.365 231.454 216.606 244.731 202.806 247.026Z"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -127,12 +127,12 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.591888 -0.80602 0.783494 -0.6214 205.531 244.745)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<path
d="M233.758 197.203L146.677 105.495"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -140,7 +140,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.814972 -0.5795 0.549892 -0.835235 250.332 138.47)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -148,7 +148,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.686415 0.72721 -0.700262 -0.713886 247.41 142.518)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -156,7 +156,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.528012 0.849237 -0.830187 -0.557485 233.775 85.838)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -164,7 +164,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.979793 -0.200014 0.185721 0.982602 147.098 164.785)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -172,7 +172,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.532065 0.846704 -0.827428 -0.561572 201.637 35.2307)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -180,7 +180,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.503861 0.863784 -0.846088 0.533044 145.445 29.1161)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -188,7 +188,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.0852204 -0.996362 0.99576 0.0919863 172.418 131.385)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -196,7 +196,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(-0.998935 0.0461398 -0.0427267 -0.999087 233.775 83.8137)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<line
y1="-2.5"
@ -204,7 +204,7 @@ export default function StaticLogo({ width, height }: LogoProps) {
y2="-2.5"
transform="matrix(0.165686 -0.986178 0.983932 0.178541 182.158 215.393)"
stroke="#C15E50"
stroke-width="5"
strokeWidth="5"
/>
<circle
cx="178.934"

View File

@ -42,7 +42,6 @@ const data = {
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const user = useUser();
console.log(user);
return (
<Sidebar

View File

@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import React from "react";
import { cn } from "../../lib/utils";
import { Loader } from "lucide-react";
import { LoaderCircle } from "lucide-react";
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded transition-colors focus-visible:outline-none focus-visible:shadow-none disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-slate-300",
@ -78,7 +78,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
{...props}
disabled={isLoading ?? disabled}
>
{isLoading ? <Loader className="mr-2 animate-spin" /> : <></>}
{isLoading ? <LoaderCircle className="mr-2 animate-spin" /> : <></>}
{children}
</Comp>
);

View File

@ -0,0 +1,25 @@
import * as SliderPrimitive from "@radix-ui/react-slider";
import * as React from "react";
import { cn } from "~/lib/utils";
const Slider = React.forwardRef<
React.ElementRef<typeof SliderPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
>(({ className, ...props }, ref) => (
<SliderPrimitive.Root
ref={ref}
className={cn(
"relative flex w-full touch-none items-center select-none",
className,
)}
{...props}
>
<SliderPrimitive.Track className="bg-primary/20 relative h-1.5 w-full grow overflow-hidden rounded-full">
<SliderPrimitive.Range className="bg-primary absolute h-full" />
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="border-primary/50 bg-background focus-visible:ring-ring block h-4 w-4 rounded-full border shadow transition-colors focus-visible:ring-1 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50" />
</SliderPrimitive.Root>
));
Slider.displayName = SliderPrimitive.Root.displayName;
export { Slider };

View File

@ -0,0 +1,44 @@
import { useState, useEffect } from "react";
/**
* A hook that persists state to localStorage
* @param key - The localStorage key to store the value under
* @param defaultValue - The default value to use if nothing is stored
* @returns A tuple of [value, setValue] similar to useState
*/
export function usePersistentState<T>(
key: string,
defaultValue: T
): [T, (value: T | ((prevValue: T) => T)) => void] {
const [state, setState] = useState<T>(() => {
// Only access localStorage on the client side
if (typeof window === "undefined") {
return defaultValue;
}
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.warn(`Error reading localStorage key "${key}":`, error);
return defaultValue;
}
});
const setValue = (value: T | ((prevValue: T) => T)) => {
try {
// Allow value to be a function so we have the same API as useState
const valueToStore = value instanceof Function ? value(state) : value;
setState(valueToStore);
// Save to localStorage on the client side
if (typeof window !== "undefined") {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
console.warn(`Error setting localStorage key "${key}":`, error);
}
};
return [state, setValue];
}

View File

@ -35,7 +35,6 @@ setInterval(
// MCP request body schema
const MCPRequestSchema = z.object({}).passthrough();
const SourceParams = z.object({
source: z.string().optional(),
});
@ -63,7 +62,9 @@ const handleMCPRequest = async (
) => {
const sessionId = request.headers.get("mcp-session-id") as string | undefined;
const source =
request.headers.get("source") || (params.source as string | undefined);
(request.headers.get("source") as string | undefined) ??
(params.source as string | undefined);
if (!source) {
return json(
{
@ -257,7 +258,12 @@ const { action, loader } = createHybridActionApiRoute(
const method = request.method;
if (method === "POST") {
return await handleMCPRequest(request, body, authentication, searchParams);
return await handleMCPRequest(
request,
body,
authentication,
searchParams,
);
} else if (method === "DELETE") {
return await handleDelete(request, authentication);
} else {

View File

@ -138,7 +138,7 @@ export default function IntegrationDetail() {
const hasApiKey = !!specData?.auth?.api_key;
const hasOAuth2 = !!specData?.auth?.OAuth2;
const hasMCPAuth = !!(
specData?.mcp.type === "url" && specData?.mcp.needsAuth
specData?.mcp.type === "http" && specData?.mcp.needsAuth
);
const Component = getIcon(integration.icon as IconType);
@ -163,84 +163,86 @@ export default function IntegrationDetail() {
},
]}
/>
<div className="h-[calc(100vh_-_56px)] overflow-hidden p-4 px-5">
<Section
title={integration.name}
description={integration.description}
icon={
<div className="bg-grayAlpha-100 flex h-12 w-12 items-center justify-center rounded">
<Component size={24} />
</div>
}
>
<div>
{/* Authentication Methods */}
<div className="space-y-4">
<h3 className="text-lg font-medium">Authentication Methods</h3>
<div className="space-y-2">
{hasApiKey && (
<div className="flex items-center gap-2">
<span className="inline-flex items-center gap-2 text-sm">
<Checkbox checked /> API Key authentication
</span>
</div>
)}
{hasOAuth2 && (
<div className="flex items-center gap-2">
<span className="inline-flex items-center gap-2 text-sm">
<Checkbox checked />
OAuth 2.0 authentication
</span>
</div>
)}
{!hasApiKey && !hasOAuth2 && !hasMCPAuth && (
<div className="text-muted-foreground text-sm">
No authentication method specified
</div>
)}
<div className="flex h-[calc(100vh_-_56px)] flex-col items-center overflow-hidden p-4 px-5">
<div className="max-w-5xl">
<Section
title={integration.name}
description={integration.description}
icon={
<div className="bg-grayAlpha-100 flex h-12 w-12 items-center justify-center rounded">
<Component size={24} />
</div>
</div>
{/* Connect Section */}
{!activeAccount && (hasApiKey || hasOAuth2) && (
<div className="mt-6 space-y-4">
<h3 className="text-lg font-medium">
Connect to {integration.name}
</h3>
{/* API Key Authentication */}
<ApiKeyAuthSection
integration={integration}
specData={specData}
activeAccount={activeAccount}
/>
{/* OAuth Authentication */}
<OAuthAuthSection
integration={integration}
specData={specData}
activeAccount={activeAccount}
/>
}
>
<div>
{/* Authentication Methods */}
<div className="space-y-4">
<h3 className="text-lg font-medium">Authentication Methods</h3>
<div className="space-y-2">
{hasApiKey && (
<div className="flex items-center gap-2">
<span className="inline-flex items-center gap-2 text-sm">
<Checkbox checked /> API Key authentication
</span>
</div>
)}
{hasOAuth2 && (
<div className="flex items-center gap-2">
<span className="inline-flex items-center gap-2 text-sm">
<Checkbox checked />
OAuth 2.0 authentication
</span>
</div>
)}
{!hasApiKey && !hasOAuth2 && !hasMCPAuth && (
<div className="text-muted-foreground text-sm">
No authentication method specified
</div>
)}
</div>
</div>
)}
{/* Connected Account Info */}
<ConnectedAccountSection activeAccount={activeAccount} />
{/* Connect Section */}
{!activeAccount && (hasApiKey || hasOAuth2) && (
<div className="mt-6 space-y-4">
<h3 className="text-lg font-medium">
Connect to {integration.name}
</h3>
{/* MCP Authentication Section */}
<MCPAuthSection
integration={integration}
activeAccount={activeAccount as any}
hasMCPAuth={hasMCPAuth}
/>
{/* API Key Authentication */}
<ApiKeyAuthSection
integration={integration}
specData={specData}
activeAccount={activeAccount}
/>
{/* Ingestion Rule Section */}
<IngestionRuleSection
ingestionRule={ingestionRule}
activeAccount={activeAccount}
/>
</div>
</Section>
{/* OAuth Authentication */}
<OAuthAuthSection
integration={integration}
specData={specData}
activeAccount={activeAccount}
/>
</div>
)}
{/* Connected Account Info */}
<ConnectedAccountSection activeAccount={activeAccount} />
{/* MCP Authentication Section */}
<MCPAuthSection
integration={integration}
activeAccount={activeAccount as any}
hasMCPAuth={hasMCPAuth}
/>
{/* Ingestion Rule Section */}
<IngestionRuleSection
ingestionRule={ingestionRule}
activeAccount={activeAccount}
/>
</div>
</Section>
</div>
</div>
</div>
);

View File

@ -14,8 +14,17 @@ import { Button } from "~/components/ui/button";
import { Card, CardContent } from "~/components/ui/card";
import { Arrows } from "~/components/icons";
import Logo from "~/components/logo/logo";
import { AlignLeft, LayoutGrid, Pen, User, Mail, Shield, Database } from "lucide-react";
import {
AlignLeft,
LayoutGrid,
Pen,
User,
Mail,
Shield,
Database,
LoaderCircle,
} from "lucide-react";
import { useState } from "react";
export const loader = async ({ request }: LoaderFunctionArgs) => {
// Check if user is authenticated
@ -36,13 +45,16 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
// Handle both space-separated (from URL encoding) and comma-separated scopes
if (scopeParam) {
// First, try splitting by spaces (common in OAuth2 URLs)
let scopes = scopeParam.split(/\s+/).filter(s => s.length > 0);
let scopes = scopeParam.split(/\s+/).filter((s) => s.length > 0);
// If no spaces found, try splitting by commas
if (scopes.length === 1) {
scopes = scopeParam.split(",").map(s => s.trim()).filter(s => s.length > 0);
scopes = scopeParam
.split(",")
.map((s) => s.trim())
.filter((s) => s.length > 0);
}
scopeParam = scopes.join(",");
} else {
throw new Error("Scope is not found");
@ -85,7 +97,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
}
// Validate scopes
if (!oauth2Service.validateScopes(client, params.scope || '')) {
if (!oauth2Service.validateScopes(client, params.scope || "")) {
return redirect(
`${params.redirect_uri}?error=${OAuth2Errors.INVALID_SCOPE}&error_description=Invalid scope${params.state ? `&state=${params.state}` : ""}`,
);
@ -151,7 +163,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
codeChallenge: params.code_challenge,
codeChallengeMethod: params.code_challenge_method,
workspaceId: workspace.id,
});
});
// Redirect back to client with authorization code
const redirectUrl = new URL(params.redirect_uri);
redirectUrl.searchParams.set("code", authCode);
@ -173,8 +185,8 @@ export const action = async ({ request }: ActionFunctionArgs) => {
};
export default function OAuthAuthorize() {
const { user, client, params } = useLoaderData<typeof loader>();
const { user, client, params } = useLoaderData<typeof loader>();
const [isRedirecting, setIsRedirecting] = useState(false);
const getScopeIcon = (scope: string) => {
switch (scope) {
@ -255,68 +267,93 @@ export default function OAuthAuthorize() {
className={`flex items-center gap-2 border-x border-t border-gray-300 p-2 ${isLast ? "border-b" : ""} ${isFirst ? "rounded-tl-md rounded-tr-md" : ""} ${isLast ? "rounded-br-md rounded-bl-md" : ""} `}
>
<div>{getScopeIcon(trimmedScope)}</div>
<div>
{getScopeDescription(trimmedScope)}
</div>
<div>{getScopeDescription(trimmedScope)}</div>
</li>
);
})}
</ul>
<Form method="post" className="space-y-3">
<input type="hidden" name="client_id" value={params.client_id} />
<input
type="hidden"
name="redirect_uri"
value={params.redirect_uri}
/>
<input
type="hidden"
name="response_type"
value={params.response_type}
/>
{params.scope && (
<input type="hidden" name="scope" value={params.scope} />
)}
{params.state && (
<input type="hidden" name="state" value={params.state} />
)}
{params.code_challenge && (
<input
type="hidden"
name="code_challenge"
value={params.code_challenge}
/>
)}
{params.code_challenge_method && (
<input
type="hidden"
name="code_challenge_method"
value={params.code_challenge_method}
/>
)}
<div className="flex justify-end space-x-3">
<Button
type="submit"
name="action"
value="deny"
size="lg"
variant="secondary"
>
Deny
</Button>
<Button
type="submit"
name="action"
value="allow"
size="lg"
className="shadow-none"
>
Allow Access
</Button>
{isRedirecting ? (
<div className="flex flex-col items-center justify-center py-8">
<LoaderCircle className="text-primary mb-2 h-4 w-4 animate-spin" />
<span className="text-muted-foreground text-sm">
Redirecting to the page... (Close this page if it doesn't
redirect in 5 seconds)
</span>
</div>
</Form>
) : (
<Form
method="post"
className="space-y-3"
onSubmit={(e) => {
// Only show loading if allow is clicked
const form = e.target as HTMLFormElement;
const allowBtn = form.querySelector(
'button[name="action"][value="allow"]',
);
if ((e.nativeEvent as SubmitEvent).submitter === allowBtn) {
setIsRedirecting(true);
}
}}
>
<input
type="hidden"
name="client_id"
value={params.client_id}
/>
<input
type="hidden"
name="redirect_uri"
value={params.redirect_uri}
/>
<input
type="hidden"
name="response_type"
value={params.response_type}
/>
{params.scope && (
<input type="hidden" name="scope" value={params.scope} />
)}
{params.state && (
<input type="hidden" name="state" value={params.state} />
)}
{params.code_challenge && (
<input
type="hidden"
name="code_challenge"
value={params.code_challenge}
/>
)}
{params.code_challenge_method && (
<input
type="hidden"
name="code_challenge_method"
value={params.code_challenge_method}
/>
)}
<div className="flex justify-end space-x-3">
<Button
type="submit"
name="action"
value="deny"
size="lg"
variant="secondary"
>
Deny
</Button>
<Button
type="submit"
name="action"
value="allow"
size="lg"
className="shadow-none"
>
Allow Access
</Button>
</div>
</Form>
)}
</div>
</CardContent>
</Card>

View File

@ -6,7 +6,7 @@ const ONE_DAY = 60 * 60 * 24;
export const { commitSession, getSession } = createCookieSessionStorage({
cookie: {
name: "__redirectTo",
name: "__redirectTo__core",
path: "/",
httpOnly: true,
sameSite: "lax",

View File

@ -9,7 +9,7 @@ export const sessionStorage = createCookieSessionStorage<{
[SESSION_KEY]: AuthUser;
}>({
cookie: {
name: "__session", // use any name you want here
name: "__session__core", // use any name you want here
sameSite: "lax", // this helps with CSRF
path: "/", // remember to add this so the cookie will work in all routes
httpOnly: true, // for security reasons, make this cookie http only

View File

@ -6,8 +6,8 @@
"scripts": {
"build": "remix vite:build",
"dev": "node ./server.mjs",
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"lint:fix": "eslint --fix --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"lint": "eslint --fix --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
"lint:fix": "eslint 'app/**/*.{ts,tsx,js,jsx}' --rule 'turbo/no-undeclared-env-vars:error' -f table",
"start": "remix-serve ./build/server/index.js",
"typecheck": "tsc",
"trigger:dev": "pnpm dlx trigger.dev@4.0.0-v4-beta.22 dev",
@ -38,6 +38,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.1.7",

View File

@ -1,16 +1,16 @@
<svg width="282" height="282" viewBox="0 0 282 282" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M80.0827 34.974C92.7457 3.8081 120.792 17.7546 134.476 29.5676C135.325 30.301 135.792 31.3761 135.792 32.4985V250.806C135.792 251.98 135.258 253.117 134.349 253.858C103.339 279.155 85.2835 259.158 80.0827 245.771C44.9187 241.11 43.965 209.932 47.8837 194.925C15.173 187.722 17.5591 152.731 22.841 136.135C9.34813 107.747 33.9141 90.4097 47.8837 85.2899C40.524 50.5456 66.2831 37.2692 80.0827 34.974Z" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(0.591888 0.80602 -0.783494 0.6214 77.3574 37.2551)" stroke="#C15E50" stroke-width="5"/>
<path d="M49.1309 84.7972L136.212 176.505" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(0.814972 0.5795 -0.549892 0.835235 32.5566 143.53)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(0.686415 -0.72721 0.700262 0.713886 35.4785 139.482)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(0.528012 -0.849237 0.830187 0.557485 49.1133 196.162)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(-0.979793 0.200014 -0.185721 -0.982602 135.791 117.215)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(0.532065 -0.846704 0.827428 0.561572 81.252 246.769)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(-0.503861 -0.863784 0.846088 -0.533044 137.443 252.884)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(-0.0852204 0.996362 -0.99576 -0.0919863 110.471 150.615)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(0.998935 -0.0461398 0.0427267 0.999087 49.1133 198.186)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(-0.165686 0.986178 -0.983932 -0.178541 100.73 66.6073)" stroke="#C15E50" stroke-width="5"/>
<path d="M80.0827 34.974C92.7457 3.8081 120.792 17.7546 134.476 29.5676C135.325 30.301 135.792 31.3761 135.792 32.4985V250.806C135.792 251.98 135.258 253.117 134.349 253.858C103.339 279.155 85.2835 259.158 80.0827 245.771C44.9187 241.11 43.965 209.932 47.8837 194.925C15.173 187.722 17.5591 152.731 22.841 136.135C9.34813 107.747 33.9141 90.4097 47.8837 85.2899C40.524 50.5456 66.2831 37.2692 80.0827 34.974Z" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(0.591888 0.80602 -0.783494 0.6214 77.3574 37.2551)" stroke="#C15E50" strokeWidth="5"/>
<path d="M49.1309 84.7972L136.212 176.505" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(0.814972 0.5795 -0.549892 0.835235 32.5566 143.53)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(0.686415 -0.72721 0.700262 0.713886 35.4785 139.482)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(0.528012 -0.849237 0.830187 0.557485 49.1133 196.162)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(-0.979793 0.200014 -0.185721 -0.982602 135.791 117.215)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(0.532065 -0.846704 0.827428 0.561572 81.252 246.769)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(-0.503861 -0.863784 0.846088 -0.533044 137.443 252.884)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(-0.0852204 0.996362 -0.99576 -0.0919863 110.471 150.615)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(0.998935 -0.0461398 0.0427267 0.999087 49.1133 198.186)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(-0.165686 0.986178 -0.983932 -0.178541 100.73 66.6073)" stroke="#C15E50" strokeWidth="5"/>
<circle cx="103.955" cy="66.3968" r="11.0444" fill="#C15E50"/>
<circle cx="90.4996" cy="129.403" r="11.5465" fill="#C15E50"/>
<circle cx="103.955" cy="197.449" r="11.0444" fill="#C15E50"/>
@ -19,18 +19,18 @@
<circle cx="29.0525" cy="140.996" r="13.0525" fill="#C15E50"/>
<circle cx="79.0009" cy="246.875" r="7.02828" fill="#C15E50"/>
<path d="M53.0314 195.433C53.0314 199.869 49.4352 203.466 44.9991 203.466C40.563 203.466 36.9668 199.869 36.9668 195.433C36.9668 190.997 40.563 187.401 44.9991 187.401C49.4352 187.401 53.0314 190.997 53.0314 195.433Z" fill="#C15E50"/>
<path d="M202.806 247.026C190.143 278.192 162.097 264.245 148.413 252.432C147.563 251.699 147.097 250.624 147.097 249.501V31.1935C147.097 30.0203 147.631 28.8833 148.54 28.1417C179.549 2.84476 197.605 22.8418 202.806 36.2294C237.97 40.8903 238.924 72.0684 235.005 87.0748C267.716 94.2779 265.33 129.269 260.048 145.865C273.541 174.253 248.975 191.59 235.005 196.71C242.365 231.454 216.606 244.731 202.806 247.026Z" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(-0.591888 -0.80602 0.783494 -0.6214 205.531 244.745)" stroke="#C15E50" stroke-width="5"/>
<path d="M233.758 197.203L146.677 105.495" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(-0.814972 -0.5795 0.549892 -0.835235 250.332 138.47)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(-0.686415 0.72721 -0.700262 -0.713886 247.41 142.518)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(-0.528012 0.849237 -0.830187 -0.557485 233.775 85.838)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(0.979793 -0.200014 0.185721 0.982602 147.098 164.785)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(-0.532065 0.846704 -0.827428 -0.561572 201.637 35.2307)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(0.503861 0.863784 -0.846088 0.533044 145.445 29.1161)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(0.0852204 -0.996362 0.99576 0.0919863 172.418 131.385)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(-0.998935 0.0461398 -0.0427267 -0.999087 233.775 83.8137)" stroke="#C15E50" stroke-width="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(0.165686 -0.986178 0.983932 0.178541 182.158 215.393)" stroke="#C15E50" stroke-width="5"/>
<path d="M202.806 247.026C190.143 278.192 162.097 264.245 148.413 252.432C147.563 251.699 147.097 250.624 147.097 249.501V31.1935C147.097 30.0203 147.631 28.8833 148.54 28.1417C179.549 2.84476 197.605 22.8418 202.806 36.2294C237.97 40.8903 238.924 72.0684 235.005 87.0748C267.716 94.2779 265.33 129.269 260.048 145.865C273.541 174.253 248.975 191.59 235.005 196.71C242.365 231.454 216.606 244.731 202.806 247.026Z" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="95.4357" y2="-2.5" transform="matrix(-0.591888 -0.80602 0.783494 -0.6214 205.531 244.745)" stroke="#C15E50" strokeWidth="5"/>
<path d="M233.758 197.203L146.677 105.495" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="90.8224" y2="-2.5" transform="matrix(-0.814972 -0.5795 0.549892 -0.835235 250.332 138.47)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="146.141" y2="-2.5" transform="matrix(-0.686415 0.72721 -0.700262 -0.713886 247.41 142.518)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="77.4689" y2="-2.5" transform="matrix(-0.528012 0.849237 -0.830187 -0.557485 233.775 85.838)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="111.328" y2="-2.5" transform="matrix(0.979793 -0.200014 0.185721 0.982602 147.098 164.785)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="58.5744" y2="-2.5" transform="matrix(-0.532065 0.846704 -0.827428 -0.561572 201.637 35.2307)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.5426" y2="-2.5" transform="matrix(0.503861 0.863784 -0.846088 0.533044 145.445 29.1161)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="45.7129" y2="-2.5" transform="matrix(0.0852204 -0.996362 0.99576 0.0919863 172.418 131.385)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="87.7453" y2="-2.5" transform="matrix(-0.998935 0.0461398 -0.0427267 -0.999087 233.775 83.8137)" stroke="#C15E50" strokeWidth="5"/>
<line y1="-2.5" x2="64.6589" y2="-2.5" transform="matrix(0.165686 -0.986178 0.983932 0.178541 182.158 215.393)" stroke="#C15E50" strokeWidth="5"/>
<circle cx="178.934" cy="215.603" r="11.0444" transform="rotate(180 178.934 215.603)" fill="#C15E50"/>
<circle cx="192.389" cy="152.597" r="11.5465" transform="rotate(180 192.389 152.597)" fill="#C15E50"/>
<circle cx="178.934" cy="84.5506" r="11.0444" transform="rotate(180 178.934 84.5506)" fill="#C15E50"/>

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -32,9 +32,12 @@ services:
ports:
- "3033:3000"
depends_on:
- postgres
- redis
- neo4j
postgres:
condition: service_healthy
redis:
condition: service_started
neo4j:
condition: service_healthy
networks:
- core
@ -51,6 +54,12 @@ services:
- postgres_data:/var/lib/postgresql/data
networks:
- core
healthcheck:
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
redis:
container_name: core-redis
@ -72,6 +81,12 @@ services:
- neo4j_data:/data
networks:
- core
healthcheck:
test: ["CMD-SHELL", "cypher-shell -u $NEO4J_USERNAME -p $NEO4J_PASSWORD 'RETURN 1'"]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s
networks:
core:

View File

@ -1,6 +1,6 @@
{
"name": "@redplanethq/core",
"version": "0.1.6",
"version": "0.1.8",
"description": "A Command-Line Interface for Core",
"type": "module",
"license": "MIT",

View File

@ -2,24 +2,19 @@ import { Command } from "commander";
import { initCommand } from "../commands/init.js";
import { startCommand } from "../commands/start.js";
import { stopCommand } from "../commands/stop.js";
import { VERSION } from "./version.js";
const program = new Command();
program.name("core").description("Core CLI - A Command-Line Interface for Core").version("0.1.0");
program.name("core").description("Core CLI - A Command-Line Interface for Core").version(VERSION);
program
.command("init")
.description("Initialize Core development environment (run once)")
.action(initCommand);
program
.command("start")
.description("Start Core development environment")
.action(startCommand);
program.command("start").description("Start Core development environment").action(startCommand);
program
.command("stop")
.description("Stop Core development environment")
.action(stopCommand);
program.command("stop").description("Stop Core development environment").action(stopCommand);
program.parse(process.argv);

View File

@ -0,0 +1 @@
export const VERSION = "0.1.7";

View File

@ -10,6 +10,8 @@ import { deployTriggerTasks } from "../utils/trigger-deploy.js";
import path from "path";
import * as fs from "fs";
import { createTriggerConfigJson, initTriggerDatabase } from "../utils/database-init.js";
import { parse } from "dotenv";
import { expand } from "dotenv-expand";
export async function initCommand() {
// Display the CORE brain logo
@ -179,10 +181,17 @@ export async function initCommand() {
// Step 12: Restart root docker-compose with new configuration
try {
const file = fs.readFileSync(envPath);
const parsed = parse(file);
const envVarsExpand = expand({ parsed, processEnv: {} }).parsed || {};
console.log(envVarsExpand);
await executeCommandInteractive("docker compose up -d", {
cwd: rootDir,
message: "Starting Core services with new Trigger.dev configuration...",
showOutput: true,
env: envVarsExpand,
});
} catch (error: any) {
outro("❌ Setup failed: " + error.message);

View File

@ -1,7 +1,8 @@
import { intro, outro, note, log, confirm } from "@clack/prompts";
import { intro, outro, note, log } from "@clack/prompts";
import { executeCommandInteractive } from "../utils/docker-interactive.js";
import { printCoreBrainLogo } from "../utils/ascii.js";
import path from "path";
import * as fs from "fs";
export async function startCommand() {
// Display the CORE brain logo
@ -10,9 +11,19 @@ export async function startCommand() {
intro("🚀 Starting Core Development Environment");
// Step 1: Confirm this is the Core repository
const isCoreRepo = await confirm({
message: "Are you currently in the Core repository directory?",
});
// Check if package.json name has "core" in it, else exit
const pkgPath = path.join(process.cwd(), "package.json");
let isCoreRepo = false;
try {
if (fs.existsSync(pkgPath)) {
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
if (typeof pkg.name === "string" && pkg.name.includes("core")) {
isCoreRepo = true;
}
}
} catch (err) {
// ignore, will prompt below
}
if (!isCoreRepo) {
note(

View File

@ -1,7 +1,8 @@
import { intro, outro, log, confirm, note } from "@clack/prompts";
import { intro, outro, log, note } from "@clack/prompts";
import { executeCommandInteractive } from "../utils/docker-interactive.js";
import { printCoreBrainLogo } from "../utils/ascii.js";
import path from "path";
import * as fs from "fs";
export async function stopCommand() {
// Display the CORE brain logo
@ -10,10 +11,19 @@ export async function stopCommand() {
intro("🛑 Stopping Core Development Environment");
// Step 1: Confirm this is the Core repository
const isCoreRepo = await confirm({
message: "Are you currently in the Core repository directory?",
});
// Check if package.json name has "core" in it, else exit
const pkgPath = path.join(process.cwd(), "package.json");
let isCoreRepo = false;
try {
if (fs.existsSync(pkgPath)) {
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
if (typeof pkg.name === "string" && pkg.name.includes("core")) {
isCoreRepo = true;
}
}
} catch (err) {
// ignore, will prompt below
}
if (!isCoreRepo) {
note(
'Please navigate to the Core repository first:\n\ngit clone https://github.com/redplanethq/core.git\ncd core\n\nThen run "core stop" again.',

View File

@ -1,4 +1,5 @@
import chalk from "chalk";
import { VERSION } from "../cli/version.js";
export function printCoreBrainLogo(): void {
const brain = `
@ -20,5 +21,9 @@ export function printCoreBrainLogo(): void {
`;
console.log(chalk.cyan(brain));
console.log(chalk.bold.white(" 🧠 CORE - Contextual Observation & Recall Engine \n"));
console.log(
chalk.bold.white(
` 🧠 CORE - Contextual Observation & Recall Engine ${VERSION ? chalk.gray(`(${VERSION})`) : ""}\n`
)
);
}

View File

@ -2,14 +2,14 @@
import Knex, { Knex as KnexT } from "knex";
import { v4 as uuidv4 } from "uuid";
import nodeCrypto from "node:crypto";
import dotenv from "dotenv";
import dotenvExpand from "dotenv-expand";
import { parse } from "dotenv";
import { expand } from "dotenv-expand";
import path from "node:path";
import { log } from "@clack/prompts";
import { customAlphabet } from "nanoid";
import $xdgAppPaths from "xdg-app-paths";
import { mkdirSync, writeFileSync } from "node:fs";
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
export const xdgAppPaths = $xdgAppPaths as unknown as typeof $xdgAppPaths.default;
@ -241,8 +241,10 @@ export async function initTriggerDatabase(triggerDir: string) {
const envPath = path.join(triggerDir, ".env");
log.step(`Loading environment variables from ${envPath}...`);
const envVarsExpand =
dotenvExpand.expand(dotenv.config({ path: envPath, processEnv: {} })).parsed || {};
const file = readFileSync(envPath);
const parsed = parse(file);
const envVarsExpand = expand({ parsed, processEnv: {} }).parsed || {};
// Set the encryption key from the .env file
ENCRYPTION_KEY = envVarsExpand.ENCRYPTION_KEY as string;

View File

@ -10,6 +10,8 @@ export interface CommandOptions {
export function executeCommandInteractive(command: string, options: CommandOptions): Promise<void> {
return new Promise((resolve, reject) => {
console.log(process.env);
const s = spinner();
s.start(options.message);
@ -27,7 +29,7 @@ export function executeCommandInteractive(command: string, options: CommandOptio
cwd: options.cwd,
stdio: options.showOutput ? ["ignore", "pipe", "pipe"] : "ignore",
detached: false,
env: options.env ? { ...process.env, ...options.env } : {},
env: options.env ? { ...process.env, ...options.env } : { ...process.env },
});
let output = "";

View File

@ -1,7 +1,8 @@
import path from "path";
import dotenv from "dotenv";
import dotenvExpand from "dotenv-expand";
import { parse } from "dotenv";
import { expand } from "dotenv-expand";
import * as fs from "fs";
/**
* Reads environment variables from .env file and replaces localhost URLs with host.docker.internal
@ -12,8 +13,10 @@ export async function getDockerCompatibleEnvVars(rootDir: string): Promise<Recor
try {
// Use dotenv to parse and expand variables
const envVarsExpand =
dotenvExpand.expand(dotenv.config({ path: envPath, processEnv: {} })).parsed || {};
const file = fs.readFileSync(envPath);
const parsed = parse(file);
const envVarsExpand = expand({ parsed, processEnv: {} }).parsed || {};
const getEnvValue = (key: string): string => {
return envVarsExpand[key] || "";

View File

@ -2,7 +2,6 @@ import { note, log } from "@clack/prompts";
import { executeCommandInteractive } from "./docker-interactive.js";
import { getDockerCompatibleEnvVars } from "./env-docker.js";
import path from "path";
import { createTriggerConfigJson } from "./database-init.js";
export async function deployTriggerTasks(rootDir: string): Promise<void> {
const webappDir = path.join(rootDir, "apps", "webapp");

35
pnpm-lock.yaml generated
View File

@ -114,6 +114,9 @@ importers:
'@radix-ui/react-separator':
specifier: ^1.1.7
version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.2.69))(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slider':
specifier: ^1.3.5
version: 1.3.5(@types/react-dom@18.3.7(@types/react@18.2.69))(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slot':
specifier: ^1.2.3
version: 1.2.3(@types/react@18.2.69)(react@18.3.1)
@ -3330,6 +3333,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-slider@1.3.5':
resolution: {integrity: sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-slot@1.0.0':
resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==}
peerDependencies:
@ -14014,6 +14030,25 @@ snapshots:
'@types/react': 18.2.69
'@types/react-dom': 18.3.7(@types/react@18.2.69)
'@radix-ui/react-slider@1.3.5(@types/react-dom@18.3.7(@types/react@18.2.69))(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/number': 1.1.1
'@radix-ui/primitive': 1.1.2
'@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.2.69))(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.2.69)(react@18.3.1)
'@radix-ui/react-context': 1.1.2(@types/react@18.2.69)(react@18.3.1)
'@radix-ui/react-direction': 1.1.1(@types/react@18.2.69)(react@18.3.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.2.69))(@types/react@18.2.69)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.2.69)(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.2.69)(react@18.3.1)
'@radix-ui/react-use-previous': 1.1.1(@types/react@18.2.69)(react@18.3.1)
'@radix-ui/react-use-size': 1.1.1(@types/react@18.2.69)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.2.69
'@types/react-dom': 18.3.7(@types/react@18.2.69)
'@radix-ui/react-slot@1.0.0(react@18.3.1)':
dependencies:
'@babel/runtime': 7.27.6

View File

@ -33,7 +33,6 @@ TRIGGER_DB=trigger
DB_HOST=host.docker.internal
DB_PORT=5432
DB_SCHEMA=sigma
# POSTGRES_DB=postgres