Fix: integration account handling and improve webhook event processing (#31)

* Feat: add onboarding screens

* Fix: integration account handling and improve webhook event processing

* Bump version: 0.1.12

---------

Co-authored-by: Manoj K <saimanoj58@gmail.com>
This commit is contained in:
Harshith Mullapudi 2025-07-25 11:56:44 +05:30 committed by GitHub
parent 24038a4789
commit 81c18ce9bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 449 additions and 123 deletions

View File

@ -1,4 +1,4 @@
VERSION=0.1.11
VERSION=0.1.12

View File

@ -1,18 +1,30 @@
import { Button } from "../ui";
import Logo from "../logo/logo";
import { Theme, useTheme } from "remix-themes";
import { GalleryVerticalEnd } from "lucide-react";
export function LoginPageLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10">
<div className="flex w-full max-w-sm flex-col items-center gap-2">
<div className="flex size-10 items-center justify-center rounded-md">
<Logo width={60} height={60} />
<div className="grid min-h-svh lg:grid-cols-2">
<div className="flex flex-col gap-4 p-6 md:p-10">
<div className="flex justify-center gap-2 md:justify-start">
<a href="#" className="flex items-center gap-2 font-medium">
<div className="flex size-8 items-center justify-center rounded-md">
<Logo width={60} height={60} />
</div>
C.O.R.E.
</a>
</div>
<a href="#" className="flex items-center gap-2 self-center font-medium">
<div className="font-mono">C.O.R.E.</div>
</a>
{children}
<div className="flex flex-1 items-center justify-center">
<div className="w-full max-w-sm">{children}</div>
</div>
</div>
<div className="relative hidden lg:block">
<img
src="/login.png"
alt="Image"
className="absolute inset-0 h-full w-full object-cover"
/>
</div>
</div>
);

View File

@ -0,0 +1,26 @@
import { Button } from "../ui";
import Logo from "../logo/logo";
import { Theme, useTheme } from "remix-themes";
export function LoginPageLayout({ children }: { children: React.ReactNode }) {
return (
<div
className="flex min-h-svh flex-col items-center justify-center gap-6 p-6 md:p-10"
style={{
backgroundImage: 'url("/back.png")',
backgroundSize: "cover",
backgroundPosition: "center",
}}
>
<div className="flex w-full max-w-sm flex-col items-center gap-2">
<div className="flex size-10 items-center justify-center rounded-md">
<Logo width={60} height={60} />
</div>
<a href="#" className="flex items-center gap-2 self-center font-medium">
<div className="font-mono">C.O.R.E.</div>
</a>
{children}
</div>
</div>
);
}

View File

@ -39,7 +39,7 @@ export function NavUser({ user }: { user: ExtendedUser }) {
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
<div className="grid flex-1 text-left text-sm leading-tight">
<span className="truncate font-medium">
Harshith Mullapudi
{user.displayName}
</span>
<span className="text-muted-foreground truncate text-xs">
{user.email}

View File

@ -200,22 +200,20 @@ export async function getUserByEmail(email: User["email"]) {
export function updateUser({
id,
name,
email,
marketingEmails,
referralSource,
}: Pick<User, "id" | "name" | "email"> & {
onboardingComplete,
}: Pick<User, "id" | "onboardingComplete"> & {
marketingEmails?: boolean;
referralSource?: string;
}) {
return prisma.user.update({
where: { id },
data: {
name,
email,
marketingEmails,
referralSource,
confirmedBasicDetails: true,
onboardingComplete,
},
});
}

View File

@ -2,7 +2,11 @@ import { redirect, type MetaFunction } from "@remix-run/node";
import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { requireUser } from "~/services/session.server";
import { confirmBasicDetailsPath, dashboardPath } from "~/utils/pathBuilder";
import {
confirmBasicDetailsPath,
dashboardPath,
onboardingPath,
} from "~/utils/pathBuilder";
export const meta: MetaFunction = () => {
return [
@ -17,6 +21,8 @@ export const loader = async ({ request }: LoaderFunctionArgs) => {
//you have to confirm basic details before you can do anything
if (!user.confirmedBasicDetails) {
return redirect(confirmBasicDetailsPath());
} else if (!user.onboardingComplete) {
return redirect(onboardingPath());
} else {
return redirect(dashboardPath());
}

View File

@ -1,10 +1,10 @@
import { json } from "@remix-run/node";
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { createHybridActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { addToQueue } from "~/lib/ingest.server";
import { IngestBodyRequest } from "~/trigger/ingest/ingest";
const { action, loader } = createActionApiRoute(
const { action, loader } = createHybridActionApiRoute(
{
body: IngestBodyRequest,
allowJWT: true,

View File

@ -56,7 +56,7 @@ const { action, loader } = createHybridActionApiRoute(
workspace?.id,
);
if (!setupResult || !setupResult.accountId) {
if (!setupResult.account || !setupResult.account.id) {
return json(
{ error: "Failed to setup integration with the provided API key" },
{ status: 400 },
@ -64,7 +64,7 @@ const { action, loader } = createHybridActionApiRoute(
}
await tasks.trigger<typeof scheduler>("scheduler", {
integrationAccountId: setupResult?.id,
integrationAccountId: setupResult?.account?.id,
});
return json({ success: true, setupResult });

View File

@ -1,6 +1,10 @@
import { z } from "zod";
import { useActionData } from "@remix-run/react";
import { type ActionFunctionArgs, json } from "@remix-run/node";
import {
type ActionFunctionArgs,
json,
type LoaderFunctionArgs,
} from "@remix-run/node";
import { useForm } from "@conform-to/react";
import { getFieldsetConstraint, parse } from "@conform-to/zod";
import { LoginPageLayout } from "~/components/layout/login-page-layout";
@ -14,10 +18,11 @@ import {
import { Button } from "~/components/ui";
import { Input } from "~/components/ui/input";
import { useState } from "react";
import { requireUserId } from "~/services/session.server";
import { requireUser, requireUserId } from "~/services/session.server";
import { redirectWithSuccessMessage } from "~/models/message.server";
import { rootPath } from "~/utils/pathBuilder";
import { createWorkspace } from "~/models/workspace.server";
import { typedjson } from "remix-typedjson";
const schema = z.object({
workspaceName: z
@ -55,6 +60,14 @@ export async function action({ request }: ActionFunctionArgs) {
}
}
export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireUser(request);
return typedjson({
user,
});
};
export default function ConfirmBasicDetails() {
const lastSubmission = useActionData<typeof action>();

View File

@ -164,7 +164,7 @@ export default function IntegrationDetail() {
]}
/>
<div className="flex h-[calc(100vh_-_56px)] flex-col items-center overflow-hidden p-4 px-5">
<div className="max-w-5xl">
<div className="w-5xl">
<Section
title={integration.name}
description={integration.description}

View File

@ -8,22 +8,31 @@ import { clearRedirectTo, commitSession } from "~/services/redirectTo.server";
import { AppSidebar } from "~/components/sidebar/app-sidebar";
import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
import { FloatingIngestionStatus } from "~/components/ingestion/floating-ingestion-status";
import { redirect } from "@remix-run/node";
import { confirmBasicDetailsPath, onboardingPath } from "~/utils/pathBuilder";
export const loader = async ({ request }: LoaderFunctionArgs) => {
const user = await requireUser(request);
const workspace = await requireWorkpace(request);
return typedjson(
{
user,
workspace,
},
{
headers: {
"Set-Cookie": await commitSession(await clearRedirectTo(request)),
//you have to confirm basic details before you can do anything
if (!user.confirmedBasicDetails) {
return redirect(confirmBasicDetailsPath());
} else if (!user.onboardingComplete) {
return redirect(onboardingPath());
} else {
return typedjson(
{
user,
workspace,
},
},
);
{
headers: {
"Set-Cookie": await commitSession(await clearRedirectTo(request)),
},
},
);
}
};
export default function Home() {

View File

@ -57,10 +57,12 @@ export default function LoginPage() {
return (
<LoginPageLayout>
<Card className="min-w-[300px] rounded-md p-3">
<Card className="w-full max-w-[350px] rounded-md bg-transparent p-3">
<CardHeader className="flex flex-col items-start">
<CardTitle>Login to your account</CardTitle>
<CardDescription>Create an account or login</CardDescription>
<CardTitle className="text-xl">Welcome back</CardTitle>
<CardDescription className="text-md">
Create an account or login
</CardDescription>
</CardHeader>
<CardContent className="pt-2">
@ -69,7 +71,7 @@ export default function LoginPage() {
{data.showGoogleAuth && (
<Button
type="submit"
size="lg"
size="xl"
variant="secondary"
className="rounded-lg text-base"
data-action="continue with google"
@ -83,7 +85,7 @@ export default function LoginPage() {
{data.emailLoginEnabled && (
<Button
variant="secondary"
size="lg"
size="xl"
data-action="continue with email"
className="text-text-bright"
onClick={() => (window.location.href = "/login/magic")}

View File

@ -13,14 +13,13 @@ import {
CardTitle,
} from "~/components/ui/card";
import { Form, useNavigation } from "@remix-run/react";
import { Inbox, Loader, LoaderCircle, Mail } from "lucide-react";
import { LoaderCircle, Mail } from "lucide-react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { z } from "zod";
import { LoginPageLayout } from "~/components/layout/login-page-layout";
import { Button } from "~/components/ui";
import { Fieldset } from "~/components/ui/Fieldset";
import { FormButtons } from "~/components/ui/FormButtons";
import { Header1 } from "~/components/ui/Headers";
import { Input } from "~/components/ui/input";
import { Paragraph } from "~/components/ui/Paragraph";
import { Cookie } from "@mjackson/headers";
@ -145,12 +144,12 @@ export default function LoginMagicLinkPage() {
<Form method="post">
<div className="flex flex-col items-center justify-center">
{data.magicLinkSent ? (
<Card className="min-w-[400px] rounded-md p-3">
<Card className="min-w-[400px] rounded-md bg-transparent p-3">
<CardHeader className="flex flex-col items-start">
<CardTitle className="mb-0 text-lg">
<CardTitle className="mb-0 text-xl">
Check your magic link
</CardTitle>
<CardDescription>
<CardDescription className="text-md">
The magic link is printed in the container logs if you are
using Docker, otherwise check your server logs.
</CardDescription>
@ -164,6 +163,7 @@ export default function LoginMagicLinkPage() {
type="submit"
name="action"
value="reset"
size="lg"
variant="secondary"
data-action="re-enter email"
>
@ -174,14 +174,14 @@ export default function LoginMagicLinkPage() {
</Fieldset>
</Card>
) : (
<Card className="min-w-[400px] rounded-md p-3">
<Card className="w-full max-w-[350px] rounded-md bg-transparent p-3">
<CardHeader className="flex flex-col items-start">
<CardTitle className="mb-0 text-lg">Welcome</CardTitle>
<CardDescription>
<CardTitle className="text-xl">Welcome back</CardTitle>
<CardDescription className="text-md">
Create an account or login using email
</CardDescription>
</CardHeader>
<CardContent className="pt-2">
<CardContent className="pt-2 pl-2">
<Fieldset className="flex w-full flex-col items-center gap-y-2">
<Input
type="email"
@ -193,28 +193,32 @@ export default function LoginMagicLinkPage() {
autoFocus
/>
<Button
name="action"
value="send"
type="submit"
variant="secondary"
size="lg"
disabled={isLoading}
data-action="send a magic link"
>
{isLoading ? (
<LoaderCircle className="text-primary h-4 w-4 animate-spin" />
) : (
<Mail className="text-text-bright mr-2 size-5" />
)}
{isLoading ? (
<span className="text-text-bright">Sending</span>
) : (
<span className="text-text-bright">
Send a magic link
</span>
)}
</Button>
<div className="flex w-full">
<Button
name="action"
value="send"
type="submit"
variant="secondary"
full
size="xl"
className="w-full"
disabled={isLoading}
data-action="send a magic link"
>
{isLoading ? (
<LoaderCircle className="text-primary h-4 w-4 animate-spin" />
) : (
<Mail className="text-text-bright mr-2 size-5" />
)}
{isLoading ? (
<span className="text-text-bright">Sending</span>
) : (
<span className="text-text-bright">
Send a magic link
</span>
)}
</Button>
</div>
{data.magicLinkError && <>{data.magicLinkError}</>}
</Fieldset>
</CardContent>

View File

@ -0,0 +1,230 @@
import { z } from "zod";
import { useLoaderData, useActionData, useNavigate } from "@remix-run/react";
import {
type ActionFunctionArgs,
json,
type LoaderFunctionArgs,
redirect,
createCookie,
} from "@remix-run/node";
import { useForm } from "@conform-to/react";
import { getFieldsetConstraint, parse } from "@conform-to/zod";
import { LoginPageLayout } from "~/components/layout/login-page-layout";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Button } from "~/components/ui";
import { Textarea } from "~/components/ui/textarea";
import { Input } from "~/components/ui/input";
import { useState } from "react";
import { requireUserId } from "~/services/session.server";
import { updateUser } from "~/models/user.server";
import { Copy, Check } from "lucide-react";
import { addToQueue } from "~/lib/ingest.server";
const ONBOARDING_STEP_COOKIE = "onboardingStep";
const onboardingStepCookie = createCookie(ONBOARDING_STEP_COOKIE, {
path: "/",
httpOnly: true,
sameSite: "lax",
maxAge: 60 * 60 * 24 * 7, // 1 week
});
const schema = z.object({
aboutUser: z
.string()
.min(
10,
"Please tell us a bit more about yourself (at least 10 characters)",
)
.max(1000, "Please keep it under 1000 characters"),
});
export async function loader({ request }: LoaderFunctionArgs) {
await requireUserId(request);
// Read step from cookie
const cookieHeader = request.headers.get("Cookie");
const cookie = (await onboardingStepCookie.parse(cookieHeader)) || {};
const step = cookie.step || null;
return json({ step });
}
export async function action({ request }: ActionFunctionArgs) {
const userId = await requireUserId(request);
const formData = await request.formData();
const submission = parse(formData, { schema });
if (!submission.value || submission.intent !== "submit") {
return json(submission);
}
const { aboutUser } = submission.value;
try {
// Ingest memory via API call
const memoryResponse = await addToQueue(
{
source: "Core",
episodeBody: aboutUser,
referenceTime: new Date().toISOString(),
},
userId,
);
if (!memoryResponse.id) {
throw new Error("Failed to save memory");
}
// Update user's onboarding status
await updateUser({
id: userId,
onboardingComplete: true,
});
// Set step in cookie and redirect to GET (PRG pattern)
const cookie = await onboardingStepCookie.serialize({
step: "memory-link",
});
return redirect("/onboarding", {
headers: {
"Set-Cookie": cookie,
},
});
} catch (e: any) {
return json({ errors: { body: e.message } }, { status: 400 });
}
}
export default function Onboarding() {
const loaderData = useLoaderData<{ step: string | null }>();
const lastSubmission = useActionData<typeof action>();
const navigate = useNavigate();
const [copied, setCopied] = useState(false);
const [form, fields] = useForm({
lastSubmission: lastSubmission as any,
constraint: getFieldsetConstraint(schema),
onValidate({ formData }) {
return parse(formData, { schema });
},
});
const memoryUrl = "https://core.heysol.ai/api/v1/mcp/memory";
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(memoryUrl);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error("Failed to copy:", err);
}
};
// Show memory link step after successful submission (step persisted in cookie)
if (loaderData.step === "memory-link") {
return (
<LoginPageLayout>
<Card className="min-w-[400px] rounded-lg bg-transparent p-3 pt-1">
<CardHeader className="flex flex-col items-start px-0">
<CardTitle className="px-0 text-xl">Your Memory Link</CardTitle>
<CardDescription className="text-md">
Here's your personal memory API endpoint. Copy this URL to connect
with external tools (Claude, Cursor etc).
</CardDescription>
</CardHeader>
<CardContent className="pt-2 text-base">
<div className="space-y-4">
<div>
<div className="bg-background-3 flex items-center rounded">
<Input
type="text"
id="memoryUrl"
value={memoryUrl}
readOnly
className="bg-background-3 block w-full text-base"
/>
<Button
type="button"
variant="link"
size="sm"
onClick={copyToClipboard}
className="px-3"
>
{copied ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
</div>
<Button
type="button"
variant="secondary"
size="xl"
className="w-full rounded-lg px-4 py-2"
onClick={() => navigate("/")}
>
Continue to Dashboard
</Button>
</div>
</CardContent>
</Card>
</LoginPageLayout>
);
}
return (
<LoginPageLayout>
<Card className="bg-background-2 w-full max-w-[400px] rounded-lg p-3 pt-1">
<CardHeader className="flex flex-col items-start px-0"></CardHeader>
<CardContent className="text-base">
<form method="post" {...form.props}>
<div className="space-y-4 pl-1">
<CardTitle className="text-md mb-0 -ml-1 px-0 text-xl">
Tell me about you
</CardTitle>
<div>
<Textarea
id="aboutUser"
placeholder="I'm Steve Jobs, co-founder of Apple. I helped create the iPhone, iPad, and Mac. I'm passionate about design, technology, and making products that change the world. I spent much of my life in California, working on innovative devices and inspiring creativity. I enjoy simplicity, calligraphy, and thinking differently..."
name={fields.aboutUser.name}
className="block min-h-[120px] w-full bg-transparent px-0 text-base"
rows={10}
/>
{fields.aboutUser.error && (
<div className="text-sm text-red-500">
{fields.aboutUser.error}
</div>
)}
</div>
<div className="flex justify-end">
<Button
type="submit"
variant="secondary"
size="xl"
className="rounded-lg px-4 py-2"
>
Continue
</Button>
</div>
</div>
</form>
</CardContent>
</Card>
</LoginPageLayout>
);
}

View File

@ -34,19 +34,20 @@ export async function action({ request, params }: ActionFunctionArgs) {
eventBody: typeof eventBody === 'object' ? JSON.stringify(eventBody).substring(0, 200) : eventBody,
});
const result = await webhookService.handleEvents(
// Check if the event is a URL verification challenge (Slack)
if (eventBody.type === "url_verification") {
logger.log("Responding to Slack URL verification challenge");
return json({ challenge: eventBody.challenge });
}
await webhookService.handleEvents(
sourceName,
integrationAccountId,
eventHeaders,
eventBody
);
// Handle URL verification challenge (returns different response)
if (result.challenge) {
return json({ challenge: result.challenge });
}
return json({ status: result.status });
return json({ status: 'acknowledged' }, { status: 200 });
} catch (error) {
logger.error('Webhook processing failed', { error, params });
@ -77,19 +78,20 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
eventBody: JSON.stringify(eventBody).substring(0, 200),
});
const result = await webhookService.handleEvents(
// Check if the event is a URL verification challenge (Slack)
if (eventBody.type === "url_verification") {
logger.log("Responding to Slack URL verification challenge");
return json({ challenge: eventBody.challenge });
}
await webhookService.handleEvents(
sourceName,
integrationAccountId,
eventHeaders,
eventBody
);
// Handle URL verification challenge (returns different response)
if (result.challenge) {
return json({ challenge: result.challenge });
}
return json({ status: result.status });
return json({ status: 'acknowledged' }, { status: 200 });
} catch (error) {
logger.error('Webhook GET processing failed', { error, params });

View File

@ -127,7 +127,7 @@ export async function callbackHandler(params: CallbackParams) {
},
);
const integrationAccount = await runIntegrationTrigger(
const setupResult = await runIntegrationTrigger(
integrationDefinition,
{
event: IntegrationEventType.SETUP,
@ -144,7 +144,7 @@ export async function callbackHandler(params: CallbackParams) {
);
await tasks.trigger<typeof scheduler>("scheduler", {
integrationAccountId: integrationAccount?.id,
integrationAccountId: setupResult?.account?.id,
});
return new Response(null, {

View File

@ -61,6 +61,7 @@ export async function requireUser(request: Request) {
createdAt: user.createdAt,
updatedAt: user.updatedAt,
confirmedBasicDetails: user.confirmedBasicDetails,
onboardingComplete: user.onboardingComplete,
isImpersonating: !!impersonationId,
};
}

View File

@ -21,12 +21,6 @@ export class WebhookService {
where: "WebhookService.handleEvents",
});
// Check if the event is a URL verification challenge (Slack)
if (eventBody.type === "url_verification") {
logger.log("Responding to Slack URL verification challenge");
return { challenge: eventBody.challenge, status: "verified" };
}
let integrationAccount:
| (IntegrationAccount & {
integrationDefinition: IntegrationDefinitionV2;

View File

@ -285,6 +285,13 @@ async function handleMessageResponse(
messageTypes: messages.map((m) => m.type),
});
const responses = {
activities: [],
state: undefined,
account: undefined,
unhandled: [],
} as any;
// Group messages by type
const grouped: Record<string, Message[]> = {};
for (const message of messages) {
@ -296,48 +303,55 @@ async function handleMessageResponse(
// Handle "activity" messages
if (grouped["activity"]) {
await handleActivityMessage(
const activities = await handleActivityMessage(
grouped["activity"],
integrationAccountId as string,
userId,
);
responses.activities = activities;
}
// Handle "state" messages
if (grouped["state"]) {
await handleStateMessage(
const state = await handleStateMessage(
grouped["state"],
integrationAccountId as string,
);
responses.state = state;
}
// Handle "identifier" messages
if (grouped["identifier"]) {
await handleIdentifierMessage(grouped["identifier"][0]);
const identifier = await handleIdentifierMessage(
grouped["identifier"][0],
);
return identifier;
}
// Handle "account" messages (these may involve Prisma writes)
if (grouped["account"]) {
await handleAccountMessage(
const account = await handleAccountMessage(
grouped["account"],
integrationDefinition,
workspaceId,
userId,
integrationAccountId as string,
);
responses.account = account;
}
const unhandled: Message[] = [];
// Warn for unknown message types
for (const type of Object.keys(grouped)) {
if (!["activity", "state", "identifier", "account"].includes(type)) {
for (const message of grouped[type]) {
logger.warn("Unknown message type", {
messageType: type,
message,
});
}
responses.unhandled.push(grouped[type]);
}
}
return responses;
} catch (error) {
logger.error("Failed to handle CLI message response", {
error: error instanceof Error ? error.message : "Unknown error",

View File

@ -14,6 +14,7 @@ export type AuthenticatedUser = {
createdAt: Date;
updatedAt: Date;
confirmedBasicDetails: boolean;
onboardingComplete: boolean;
authMethod: 'session' | 'pat' | 'oauth2';
oauth2?: {
clientId: string;
@ -47,6 +48,7 @@ export async function requireAuth(request: Request): Promise<AuthenticatedUser>
createdAt: user.createdAt,
updatedAt: user.updatedAt,
confirmedBasicDetails: user.confirmedBasicDetails,
onboardingComplete: user.onboardingComplete,
authMethod: 'pat',
};
}
@ -65,6 +67,7 @@ export async function requireAuth(request: Request): Promise<AuthenticatedUser>
createdAt: accessToken.user.createdAt,
updatedAt: accessToken.user.updatedAt,
confirmedBasicDetails: accessToken.user.confirmedBasicDetails,
onboardingComplete: accessToken.user.onboardingComplete,
authMethod: 'oauth2',
oauth2: {
clientId: accessToken.client.clientId,
@ -89,6 +92,7 @@ export async function requireAuth(request: Request): Promise<AuthenticatedUser>
createdAt: sessionUser.createdAt,
updatedAt: sessionUser.updatedAt,
confirmedBasicDetails: sessionUser.confirmedBasicDetails,
onboardingComplete: sessionUser.onboardingComplete,
authMethod: 'session',
};
}

View File

@ -2,6 +2,10 @@ export function confirmBasicDetailsPath() {
return `/confirm-basic-details`;
}
export function onboardingPath() {
return `/onboarding`;
}
export function homePath() {
return `/home`;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

View File

@ -58,6 +58,6 @@
"commander": "^12.0.0",
"openai": "^4.0.0",
"react-query": "^3.39.3",
"@redplanethq/sdk": "0.1.1"
"@redplanethq/sdk": "0.1.2"
}
}

View File

@ -9,8 +9,8 @@ importers:
.:
dependencies:
'@redplanethq/sdk':
specifier: 0.1.1
version: 0.1.1
specifier: 0.1.2
version: 0.1.2
axios:
specifier: ^1.7.9
version: 1.9.0
@ -465,8 +465,8 @@ packages:
resolution: {integrity: sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@redplanethq/sdk@0.1.1':
resolution: {integrity: sha512-tfR1c9p7vNeCL5jsF9QlEZcRFLsihaHe/ZQWVKZYXzAZ6GugoIFBaayGfVvjNjyEnN3nlrl3usKBX+hhaKzg0g==}
'@redplanethq/sdk@0.1.2':
resolution: {integrity: sha512-Si+ae2OV0UNy7yvLECjJ1Y/6HHhRO0yU9svIFvBzMlaR/57SDP+KJZLyfWx/PJX+hOuWipyoYTLA9aYULt6z2w==}
engines: {node: '>=18.0.0'}
'@rollup/rollup-android-arm-eabi@4.40.2':
@ -2683,7 +2683,7 @@ snapshots:
'@pkgr/core@0.2.4': {}
'@redplanethq/sdk@0.1.1':
'@redplanethq/sdk@0.1.2':
dependencies:
commander: 14.0.0

View File

@ -52,11 +52,10 @@ async function getConversationInfo(accessToken: string, channel: string) {
export const createActivityEvent = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
eventBody: any,
eventData: any,
config: any,
) => {
const { eventData } = eventBody;
if (eventData.event.type === 'message' && eventData.event.channel === 'D06UAK42494') {
if (eventData.event.type === 'message' && eventData.event.channel === 'D08TQATE3F0') {
const event = eventData.event;
if (!config) {
@ -65,7 +64,7 @@ export const createActivityEvent = async (
const accessToken = config.access_token;
const text = `DM with SOL channel Content: '${event.text}'`;
const text = `I DMed to you Content: '${event.text}'`;
const permalinkResponse = await axios.get(
`https://slack.com/api/chat.getPermalink?channel=${event.channel}&message_ts=${event.ts}`,
@ -81,7 +80,7 @@ export const createActivityEvent = async (
taskId: null,
};
return createActivityMessage(activity);
return [createActivityMessage(activity)];
}
if (eventData.event.type === 'reaction_added' && eventData.event.reaction === 'eyes') {
@ -121,7 +120,7 @@ export const createActivityEvent = async (
conversationContext = `channel ${conversationInfo.name}(${conversationInfo.id})`;
}
const text = `Message to User from ${userIdMap.get(eventMessage.user)?.real_name}(${eventMessage.user}) in ${conversationContext} at ${eventMessage.ts}. Content: '${eventMessageText}'`;
const text = `User ${userIdMap.get(eventMessage.user)?.real_name}(${eventMessage.user}) reacted with eyes emoji in ${conversationContext} at ${eventMessage.ts}. Content: '${eventMessageText}'`;
const permalinkResponse = await axios.get(
`https://slack.com/api/chat.getPermalink?channel=${channel}&message_ts=${ts}`,
@ -137,9 +136,10 @@ export const createActivityEvent = async (
integrationAccountId: config.integrationAccountId,
};
return createActivityMessage(activity);
return [createActivityMessage(activity)];
}
return { message: `Processed activity from slack` };
return [];
};
function getMentionUsers(message: string): string[] {

View File

@ -13,13 +13,20 @@ export async function run(eventPayload: IntegrationEventPayload) {
return await integrationCreate(eventPayload.eventBody);
case IntegrationEventType.IDENTIFY:
return eventPayload.eventBody.event.user;
return [
{
type: 'identifier',
data:
eventPayload.eventBody.event.event.user ||
eventPayload.eventBody.event.event.message.user,
},
];
case IntegrationEventType.PROCESS:
return createActivityEvent(eventPayload.eventBody.eventData, eventPayload.config);
default:
return { message: `The event payload type is ${eventPayload.event}` };
return [{ type: 'error', data: `The event payload type is ${eventPayload.event}` }];
}
}

View File

@ -1,7 +1,7 @@
{
"name": "core",
"private": true,
"version": "0.1.11",
"version": "0.1.12",
"workspaces": [
"apps/*",
"packages/*"

View File

@ -186,7 +186,6 @@ export async function initCommand() {
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...",

View File

@ -10,8 +10,6 @@ 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);

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "onboardingComplete" BOOLEAN NOT NULL DEFAULT false;

View File

@ -482,6 +482,7 @@ model User {
marketingEmails Boolean @default(true)
confirmedBasicDetails Boolean @default(false)
onboardingComplete Boolean @default(false)
referralSource String?

View File

@ -1,6 +1,6 @@
{
"name": "@redplanethq/sdk",
"version": "0.1.1",
"version": "0.1.2",
"description": "CORE Node.JS SDK",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@ -78,7 +78,7 @@ export abstract class IntegrationCLI {
const messages: Message[] = await this.handleEvent({
event: IntegrationEventType.PROCESS,
eventBody: { eventData },
eventBody: eventData,
config,
});