Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ import {
hasNeedsApprovalDeep,
getToolDisplayName,
} from "./conversation-utils";
import { ICON_MAPPING } from "../icon-utils";
import type { IconType } from "../icon-utils";
import { ICON_MAPPING ,type IconType } from "../icon-utils";
import { Task } from "../icons/task";

interface AIConversationItemProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EditorRoot, EditorContent, Placeholder } from "novel";
import { useState, useRef, useCallback } from "react";
import { Form, useSubmit } from "@remix-run/react";
import { cn } from "~/lib/utils";
import { EyeOff } from "lucide-react";
import { EyeOff , ArrowUp } from "lucide-react";
import { Document } from "@tiptap/extension-document";
import HardBreak from "@tiptap/extension-hard-break";
import { History } from "@tiptap/extension-history";
Expand All @@ -12,7 +12,6 @@ import { Button } from "../ui";
import { ExampleUseCases } from "./example-usecases";
import { type Editor } from "@tiptap/react";
import Logo from "../logo/logo";
import { ArrowUp } from "lucide-react";
import { RiGithubFill } from "@remixicon/react";
import { Gmail } from "../icons/gmail";
import { LinearIcon } from "../icons/linear-icon";
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/icon-utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { TodoLine } from "./icons/todo";
import { InProgressLine } from "./icons/in-progress";
import { BlockedLine } from "./icons/blocked";
import { DoneFill } from "./icons/done";
import { TaskStatus } from "@core/database";
import { type TaskStatus } from "@core/database";
import { Task } from "./icons/task";

export const ICON_MAPPING = {
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/components/ui/color-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TaskStatus } from "@prisma/client";
import { type TaskStatus } from "@prisma/client";

/**
* Generates an OKLCH color string with fixed lightness, chroma, and a random hue.
Expand Down
3 changes: 1 addition & 2 deletions apps/webapp/app/routes/api.v1.onboarding.suggestions.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { json } from "@remix-run/node";
import { generateObject } from "ai";
import { generateObject , type LanguageModel } from "ai";
import { z } from "zod";
import { type LanguageModel } from "ai";
import { createHybridActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import { getLibrarySkills } from "~/lib/skills-library";
import { getIntegrationDefinitions } from "~/services/integrationDefinition.server";
Expand Down
3 changes: 1 addition & 2 deletions apps/webapp/app/routes/home.memory.labels._index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData, useNavigate } from "@remix-run/react";
import { type NavigateFunction } from "@remix-run/react";
import { useLoaderData, useNavigate , type NavigateFunction } from "@remix-run/react";
import { useRef } from "react";
import { Tag, FileText } from "lucide-react";
import {
Expand Down
6 changes: 2 additions & 4 deletions apps/webapp/app/routes/onboarding.suggestions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import {
type ActionFunctionArgs,
type LoaderFunctionArgs,
} from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { useFetcher } from "@remix-run/react";
import { generateObject } from "ai";
import { useLoaderData , useFetcher } from "@remix-run/react";
import { generateObject , type LanguageModel } from "ai";
import { z } from "zod";
import { type LanguageModel } from "ai";
import { requireUser } from "~/services/session.server";
import { updateUser } from "~/models/user.server";
import { getLibrarySkills } from "~/lib/skills-library";
Expand Down
3 changes: 1 addition & 2 deletions apps/webapp/app/routes/settings.account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Card } from "~/components/ui/card";
import { Button } from "~/components/ui/button";
import { Input } from "~/components/ui/input";
import { Label } from "~/components/ui/label";
import { AlertTriangle, Check } from "lucide-react";
import { AlertTriangle, Check , Moon, Sun } from "lucide-react";
import { useState } from "react";
import {
AlertDialog,
Expand All @@ -24,7 +24,6 @@ import {
import { SettingSection } from "~/components/setting-section";
import { cn } from "~/lib/utils";
import { Theme, useTheme } from "remix-themes";
import { Moon, Sun } from "lucide-react";

interface SuccessDataResponse {
success: boolean;
Expand Down
3 changes: 1 addition & 2 deletions apps/webapp/app/routes/workspace.join.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
type ActionFunctionArgs,
json,
type LoaderFunctionArgs,
} from "@remix-run/node";
redirect } from "@remix-run/node";
import { useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import { LoginPageLayout } from "~/components/layout/login-page-layout";
Expand All @@ -24,7 +24,6 @@ import { ensureBillingInitialized } from "~/services/billing.server";
import { LabelService } from "~/services/label.server";
import { logger } from "~/services/logger.service";
import { saveSession } from "~/services/sessionStorage.server";
import { redirect } from "@remix-run/node";
import Logo from "~/components/logo/logo";

const schema = z.object({
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/services/agent/explorers/web-explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Exa from "exa-js";
import { logger } from "~/services/logger.service";
import { env } from "~/env.server";
import { getModel, getModelForTask } from "~/lib/model.server";
import { ExplorerResult } from "../types";
import { type ExplorerResult } from "../types";

const WEB_COMPLEXITY = "high";

Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/services/agent/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { logger } from "~/services/logger.service";
import { getModel, getModelForTask } from "~/lib/model.server";
import { getConnectedGateways, getGateway } from "~/services/gateway.server";
import { callGatewayTool } from "../../../websocket";
import { GatewayAgentInfo, type OrchestratorTools } from "~/services/agent/orchestrator-tools";
import { type GatewayAgentInfo, type OrchestratorTools } from "~/services/agent/orchestrator-tools";

// Types for gateway tools (matches schema in database)
interface GatewayTool {
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/services/agent/orchestrator-tools.http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* direct DB access is not available.
*/

import { CoreClient } from "@redplanethq/sdk";
import { type CoreClient } from "@redplanethq/sdk";
import { UserTypeEnum } from "@core/types";
import {
OrchestratorTools,
Expand Down
188 changes: 188 additions & 0 deletions apps/webapp/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import { createRequestHandler } from "@remix-run/express";
import compression from "compression";
import express from "express";
import morgan from "morgan";
import { createServer } from "http";
// import { handleMCPRequest, handleSessionRequest } from "~/services/mcp.server";
// import { authenticateHybridRequest } from "~/services/routeBuilders/apiBuilder.server";
let viteDevServer;
let remixHandler;
// Helper to get origin from request host or fallback to APP_ORIGIN
function getOrigin(req) {
const host = req.hostname;
if (host?.includes("getcore.me")) {
return `https://${host}`;
}
return process.env.APP_ORIGIN;
}
async function init() {
if (process.env.NODE_ENV !== "production") {
const vite = await import("vite");
viteDevServer = await vite.createServer({
server: { middlewareMode: true },
});
}
const build = viteDevServer
? () => viteDevServer.ssrLoadModule("virtual:remix/server-build")
: await import("./build/server/index.js");
const module = viteDevServer
? (await build()).entry.module
: build.entry?.module;
remixHandler = createRequestHandler({ build });
const app = express();
// Trust proxy headers (for AWS ALB/CloudFront)
app.set("trust proxy", true);
app.use(compression());
// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable("x-powered-by");
// handle asset requests
if (viteDevServer) {
app.use(viteDevServer.middlewares);
}
else {
// Vite fingerprints its assets so we can cache forever.
app.use("/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y" }));
}
// Everything else (like favicon.ico) is cached for an hour. You may want to be
// more aggressive with this caching.
app.use(express.static("build/client", { maxAge: "1h" }));
app.use(morgan("tiny"));
app.get("/api/v1/mcp", async (req, res) => {
const origin = getOrigin(req);
// Enable CORS for all domains
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
const authenticationResult = await module.authenticateHybridRequest(req, {
allowJWT: true,
});
if (!authenticationResult) {
// Step 1: Initial 401 handshake with WWW-Authenticate header
res.setHeader("WWW-Authenticate", `Bearer realm="mcp", resource_metadata="${origin}/.well-known/oauth-protected-resource"`);
res.status(401).json({
error: "unauthorized",
error_description: "Authentication required. See WWW-Authenticate header for authorization information.",
});
return;
}
await module.handleSessionRequest(req, res, authenticationResult.workspaceId, authenticationResult.userId);
});
app.post("/api/v1/mcp", async (req, res) => {
const origin = getOrigin(req);
// Enable CORS for all domains
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
const authenticationResult = await module.authenticateHybridRequest(req, {
allowJWT: true,
});
if (!authenticationResult) {
// Step 1: Initial 401 handshake with WWW-Authenticate header
res.setHeader("WWW-Authenticate", `Bearer realm="mcp", resource_metadata="${origin}/.well-known/oauth-protected-resource"`);
res.status(401).json({
error: "unauthorized",
error_description: "Authentication required. See WWW-Authenticate header for authorization information.",
});
return;
}
let body = "";
req.on("data", (chunk) => {
body += chunk;
});
req.on("end", async () => {
try {
const parsedBody = JSON.parse(body);
const queryParams = req.query; // Get query parameters from the request
await module.handleMCPRequest(req, res, parsedBody, authenticationResult, queryParams);
}
catch (error) {
console.log(error);
res.status(400).json({ error: "Invalid JSON" });
}
});
});
app.delete("/api/v1/mcp", async (req, res) => {
const origin = getOrigin(req);
// Enable CORS for all domains
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
const authenticationResult = await module.authenticateHybridRequest(req, {
allowJWT: true,
});
if (!authenticationResult) {
// Step 1: Initial 401 handshake with WWW-Authenticate header
res.setHeader("WWW-Authenticate", `Bearer realm="mcp", resource_metadata="${origin}/.well-known/oauth-protected-resource"`);
res.status(401).json({
error: "unauthorized",
error_description: "Authentication required. See WWW-Authenticate header for authorization information.",
});
return;
}
await module.handleSessionRequest(req, res, authenticationResult.workspaceId);
});
app.options("/api/v1/mcp", (_, res) => {
// Enable CORS for all domains
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
res.json({});
});
// Step 2: Protected Resource Metadata (PRM) endpoint
app.get("/.well-known/oauth-protected-resource", (req, res) => {
const origin = getOrigin(req);
res.json({
resource: `${origin}/api/v1/mcp`,
authorization_servers: [origin],
scopes_supported: [
"mcp",
"mcp:read",
"mcp:write",
"mcp.read",
"mcp.write",
],
bearer_methods_supported: ["header"],
resource_signing_alg_values_supported: ["HS256"],
});
});
// Step 3: Authorization Server Metadata endpoint
app.get("/.well-known/oauth-authorization-server", (req, res) => {
const origin = getOrigin(req);
res.json({
issuer: origin,
authorization_endpoint: `${origin}/oauth/authorize`,
token_endpoint: `${origin}/oauth/token`,
registration_endpoint: `${origin}/oauth/register`,
scopes_supported: [
"mcp",
"mcp:read",
"mcp:write",
"mcp.read",
"mcp.write",
],
response_types_supported: ["code"],
grant_types_supported: [
"authorization_code",
"refresh_token",
"client_credentials",
],
code_challenge_methods_supported: ["S256", "plain"],
token_endpoint_auth_methods_supported: ["client_secret_post"],
});
});
// handle SSR requests
app.all("*", remixHandler);
// Create HTTP server and setup WebSocket
const server = createServer(app);
// Setup WebSocket with gateway module functions
module.setupWebSocket(server, {
verifyGatewayToken: module.verifyGatewayToken,
upsertGateway: module.upsertGateway,
updateGatewayTools: module.updateGatewayTools,
updateGatewayLastSeen: module.updateGatewayLastSeen,
disconnectGateway: module.disconnectGateway,
});
const port = process.env.REMIX_APP_PORT || 3000;
server.listen(port, () => console.log(`Server listening at http://localhost:${port}`));
}
init().catch(console.error);
39 changes: 39 additions & 0 deletions integrations/asana/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@core/asana",
"version": "0.1.0",
"description": "Asana integration for CORE",
"main": "./bin/index.js",
"module": "./bin/index.mjs",
"type": "module",
"files": [
"asana",
"bin"
],
"bin": {
"asana": "./bin/index.js"
},
"scripts": {
"build": "rimraf dist && bun build src/index.ts --outfile dist/index.js --target node --minify",
"lint": "eslint --ext js,ts,tsx src/ --fix",
"prettier": "prettier --config .prettierrc --write ."
},
"devDependencies": {
"@types/node": "^18.0.20",
"eslint": "^9.24.0",
"prettier": "^3.4.2",
"rimraf": "^3.0.2",
"typescript": "^4.7.2",
"tsup": "^8.0.1"
},
"publishConfig": {
"access": "public"
},
"dependencies": {
"axios": "^1.7.9",
"commander": "^12.0.0",
"@redplanethq/sdk": "0.1.9",
"zod": "^3.25.4",
"zod-to-json-schema": "^3.25.1",
"pg": "^8.11.0"
}
}
Loading