@muxoai/platform 1.4.1 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +2 -2
- package/README.md +1 -1
- package/dist/cli.js +6 -3
- package/dist/composio-catalog.d.ts +177 -1
- package/dist/composio-catalog.js +1427 -7
- package/dist/config.d.ts +6 -0
- package/dist/config.js +11 -1
- package/dist/engine.d.ts +18 -1
- package/dist/engine.js +259 -35
- package/dist/mcp-schema.d.ts +162 -1
- package/dist/mcp-schema.js +1523 -40
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +412 -23
- package/dist/policy.d.ts +108 -1
- package/dist/policy.js +406 -1
- package/dist/providers/composio-instant.js +38 -3
- package/dist/providers/composio-sdk.d.ts +59 -2
- package/dist/providers/composio-sdk.js +273 -59
- package/dist/providers/stripe-projects.d.ts +4 -0
- package/dist/providers/stripe-projects.js +548 -126
- package/dist/server.js +531 -61
- package/dist/store.js +2 -0
- package/dist/types.d.ts +15 -0
- package/dist/ui/console-html.d.ts +31 -1
- package/dist/ui/console-html.js +922 -368
- package/dist/ui/design-tokens.js +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -2
- package/plugins/muxo/.claude-plugin/plugin.json +2 -2
- package/plugins/muxo/.codex-plugin/plugin.json +15 -8
- package/plugins/muxo/.cursor-plugin/plugin.json +30 -0
- package/plugins/muxo/hooks/session-start.sh +15 -3
- package/plugins/muxo/hooks/user-prompt-submit.sh +14 -1
- package/plugins/muxo/mcp.json +0 -10
- package/plugins/muxo/plugin.json +4 -4
- package/plugins/muxo/skills/muxo/SKILL.md +25 -16
- package/plugins/muxo/skills/muxo/references/cli.md +5 -1
- package/plugins/muxo/skills/muxo/references/mcp.md +11 -6
- package/plugins/muxo/skills/muxo-activity-summary/SKILL.md +81 -0
- package/plugins/muxo/skills/setup/SKILL.md +1 -1
- package/src/catalogues/stripe-projects-catalog.json +140 -0
package/dist/server.js
CHANGED
|
@@ -5,13 +5,14 @@ import path from "node:path";
|
|
|
5
5
|
import { getConfig, ROOT_DIR } from "./config.js";
|
|
6
6
|
import { MuxoEngine } from "./engine.js";
|
|
7
7
|
import { getMuxoMcpSkills, handleJsonRpcMcpRequest, MUXO_MCP_SERVER_INSTRUCTIONS, MUXO_MCP_TOOLS, } from "./mcp.js";
|
|
8
|
-
import { APPROVED_PLAN_STATUSES, MuxoToolError, clampWaitSeconds, errorPayloadFrom, isPublicMcpRequest, workspaceAgentDid, } from "./mcp-schema.js";
|
|
8
|
+
import { APPROVED_PLAN_STATUSES, LATEST_MCP_PROTOCOL_VERSION, MuxoToolError, SUPPORTED_MCP_PROTOCOL_VERSIONS, clampWaitSeconds, errorPayloadFrom, isPublicMcpRequest, validateMcpHttpHeadersAndMeta, workspaceAgentDid, } from "./mcp-schema.js";
|
|
9
9
|
import { MuxoStore } from "./store.js";
|
|
10
10
|
import { managedProviderConnectionList, managedProviderConnectionRequest, managedProviderConnectionUnlink, managedResource, managedResourceAction, } from "./provisioning-managed.js";
|
|
11
11
|
import { DEFAULT_WORKSPACE_POLICY, describePolicyInPlainEnglish, sanitizePolicyPatch, } from "./policy.js";
|
|
12
12
|
import { parseNaturalLanguageMemoryInstruction } from "./providers/supermemory.js";
|
|
13
13
|
import { MUXO_VERSION } from "./version.js";
|
|
14
|
-
import {
|
|
14
|
+
import { fetchMuxoConnectLinkMetadata, isStripeProjectsExclusiveProvider, normalizeStripeProjectsProviderSlug, parseComposioWebhookEnvelope, submitMuxoConnectLink, verifyComposioWebhookSignature, wrapMuxoConnectUrl, } from "./composio-catalog.js";
|
|
15
|
+
import { isConsolePath, renderApprovalPageHtml, renderConsoleHtml, renderLandingHtml, renderOAuthCallbackPageHtml, renderOAuthConnectPageHtml, renderOAuthConsentHtml, } from "./ui/console-html.js";
|
|
15
16
|
import { fetchClerkProfile, looksLikeJwt, readClerkConfig, verifyClerkSessionToken, } from "./clerk.js";
|
|
16
17
|
import { DEFAULT_OAUTH_SCOPE, OAUTH_ACCESS_TOKEN_TTL_SECONDS, OAUTH_CODE_TTL_MS, oauthConnectionKeyName, oauthRedirectOrigin, oauthSecretToken, parseAuthorizeRequest, parseOAuthFormBody, verifyPkce, } from "./oauth.js";
|
|
17
18
|
function renderSkillMarkdown(baseUrl) {
|
|
@@ -109,7 +110,20 @@ Users and LLM harnesses can set safety rules in natural language or structured p
|
|
|
109
110
|
- "Only post Slack messages to #ops-alerts or #deployments" → channel allowlist guardrail
|
|
110
111
|
- "Never spend more than 25 credits per run" → workspace per-run credit cap
|
|
111
112
|
|
|
112
|
-
Set or inspect them via MCP \`get_run_context({ action: "set_guardrail", prompt: "...", parameter_rules: [...] })\`,
|
|
113
|
+
Set or inspect them via MCP \`get_run_context({ action: "set_guardrail", prompt: "...", parameter_rules: [...] })\`, dry-run simulate a tool call before planning via \`get_run_context({ action: "check_policy", tool_slug: "...", input: { ... } })\`, reset guardrails via \`get_run_context({ action: "reset_guardrails" })\`, toggle the emergency kill switch via \`get_run_context({ action: "kill_switch", enabled: true, reason: "..." })\`, or scan untrusted inputs for OWASP ASI-06 prompt injection via \`get_run_context({ action: "scan_context", prompt: "..." })\`. Equivalent REST routes: \`POST /api/v1/do\`, \`GET/POST /api/v1/guardrails\`, \`POST /api/v1/guardrails/check\`, and \`POST /api/v1/guardrails/reset\`. Responses always include \`plainEnglishGuardrails\` and a structured \`llmPolicyBrief\` (\`riskLaneMatrix\`, \`parameterRules[].howToComply\`, \`llmSystemDirective\`, and \`agentControlCheatsheet\`).
|
|
114
|
+
|
|
115
|
+
## MCP Resources & Prompts for AI Harnesses
|
|
116
|
+
|
|
117
|
+
- **MCP Resources (\`resources/list\` & \`resources/read\`)**:
|
|
118
|
+
- \`muxo://policy/brief\` — Structured \`llmPolicyBrief\` (risk-lane approval matrix, parameter compliance rules, learned defaults, and agent control cheatsheet)
|
|
119
|
+
- \`muxo://governance/policy\` — Active guardrails, kill-switch status, trust score, and \`llmPolicyBrief\`
|
|
120
|
+
- \`muxo://context\` — Live workspace context, active guardrails, provisioned resources, and \`llmSystemPrompt\`
|
|
121
|
+
- \`muxo://memory/graph\` — Active Supermemory defaults and bi-temporal knowledge graph facts
|
|
122
|
+
- **MCP Prompts (\`prompts/list\` & \`prompts/get\`)**:
|
|
123
|
+
- \`context\` — Inject workspace memory, active defaults, and guardrail summary
|
|
124
|
+
- \`policy_guardrails\` — Inject the active \`llmSystemDirective\`, risk-lane matrix, and optional dry-run policy simulation for \`tool_slug\`
|
|
125
|
+
- \`governed_execution\` — Step-by-step Plan → Approve → Execute → Receipt system instructions
|
|
126
|
+
- \`security_audit\` — OWASP Agentic Top 10 security and receipt audit workflow
|
|
113
127
|
|
|
114
128
|
## Active Supermemory (teach Muxo once, auto-fill forever)
|
|
115
129
|
|
|
@@ -134,8 +148,8 @@ function sendJson(res, status, payload, extraHeaders) {
|
|
|
134
148
|
res.writeHead(status, {
|
|
135
149
|
"Content-Type": "application/json; charset=utf-8",
|
|
136
150
|
"Access-Control-Allow-Origin": "*",
|
|
137
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, Mcp-Session-Id",
|
|
138
|
-
"Access-Control-Expose-Headers": "Mcp-Session-Id, WWW-Authenticate",
|
|
151
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Muxo-Workspace-Id, Mcp-Session-Id, mcp-session-id, MCP-Protocol-Version, mcp-protocol-version, Mcp-Method, mcp-method, Mcp-Name, mcp-name",
|
|
152
|
+
"Access-Control-Expose-Headers": "Mcp-Session-Id, MCP-Protocol-Version, WWW-Authenticate",
|
|
139
153
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
140
154
|
...(extraHeaders || {}),
|
|
141
155
|
});
|
|
@@ -320,7 +334,8 @@ export function createMuxoServer(customConfig) {
|
|
|
320
334
|
if (req.method === "OPTIONS") {
|
|
321
335
|
res.writeHead(204, {
|
|
322
336
|
"Access-Control-Allow-Origin": "*",
|
|
323
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Muxo-Workspace-Id",
|
|
337
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Muxo-Workspace-Id, Mcp-Session-Id, mcp-session-id, MCP-Protocol-Version, mcp-protocol-version, Mcp-Method, mcp-method, Mcp-Name, mcp-name",
|
|
338
|
+
"Access-Control-Expose-Headers": "Mcp-Session-Id, MCP-Protocol-Version, WWW-Authenticate",
|
|
324
339
|
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
|
|
325
340
|
});
|
|
326
341
|
res.end();
|
|
@@ -746,8 +761,14 @@ export function createMuxoServer(customConfig) {
|
|
|
746
761
|
"/api/v1/governance/kill-switch": { post: { summary: "Pause or resume workspace execution via kill switch" } },
|
|
747
762
|
"/api/v1/governance/scan-context": { post: { summary: "Scan untrusted text for prompt injection before storage or use" } },
|
|
748
763
|
"/api/v1/guardrails": {
|
|
749
|
-
get: { summary: "List active plain-English guardrails, policy, and learned memory defaults" },
|
|
750
|
-
post: { summary: "Set or reset guardrails in plain English
|
|
764
|
+
get: { summary: "List active plain-English guardrails, llmPolicyBrief, policy, and learned memory defaults" },
|
|
765
|
+
post: { summary: "Set or reset guardrails in plain English or structured parameterRules" },
|
|
766
|
+
},
|
|
767
|
+
"/api/v1/guardrails/check": {
|
|
768
|
+
post: { summary: "Dry-run evaluate a tool call and arguments against active workspace policy, conditional parameter rules, and memory defaults" },
|
|
769
|
+
},
|
|
770
|
+
"/api/v1/guardrails/reset": {
|
|
771
|
+
post: { summary: "Reset workspace guardrails and parameter rules to Safe Default" },
|
|
751
772
|
},
|
|
752
773
|
"/approve/{planId}": { get: { summary: "One-click human approval handoff page for a pending plan" } },
|
|
753
774
|
"/api/v1/context": {
|
|
@@ -806,10 +827,10 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
806
827
|
}
|
|
807
828
|
if (pathname === "/llms.txt" && req.method === "GET") {
|
|
808
829
|
res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
|
|
809
|
-
res.end(`# Muxo\n> Unified agent execution across Connected Apps, Instant Tools, Cloud Infrastructure, Workspace Memory, and Safety Guardrails.\n\n- Skill Document: ${config.baseUrl}/skill.md\n- Auth Guide: ${config.baseUrl}/auth.md\n- OpenAPI 3.1 Spec: ${config.baseUrl}/api/openapi.json\n- MCP Endpoint: ${config.baseUrl}/mcp\n- OAuth Callback Handoff: ${config.baseUrl}/oauth/callback\n- Plugin Marketplace (Codex/OpenAI): ${config.baseUrl}/.agents/plugins/marketplace.json\n- Plugin Marketplace (Claude/Droid): ${config.baseUrl}/.claude-plugin/marketplace.json\n- Console (app.muxo.ai): ${config.baseUrl}/\n- Landing & Provider Canvas (muxo.ai): ${config.baseUrl}/landing\n- Documentation (docs.muxo.ai): ${config.docsUrl}\n`);
|
|
830
|
+
res.end(`# Muxo\n> Unified agent execution across Connected Apps, Instant Tools, Cloud Infrastructure, Workspace Memory, and Safety Guardrails.\n\n- Skill Document: ${config.baseUrl}/skill.md\n- Auth Guide: ${config.baseUrl}/auth.md\n- OpenAPI 3.1 Spec: ${config.baseUrl}/api/openapi.json\n- MCP Endpoint: ${config.baseUrl}/mcp\n- OAuth Callback Handoff: ${config.baseUrl}/oauth/callback\n- Plugin Marketplace (Codex/OpenAI): ${config.baseUrl}/.agents/plugins/marketplace.json\n- Plugin Marketplace (Claude/Droid): ${config.baseUrl}/.claude-plugin/marketplace.json\n- Console (app.muxo.ai): ${config.baseUrl}/\n- Landing & Provider Canvas (muxo.ai): ${config.baseUrl}/landing\n- Documentation (docs.muxo.ai): ${config.docsUrl}\n\n## Agent-Native Policy, Context & Governance Interface\n- MCP Control Plane (\`get_run_context\`): \`action: "recall" | "remember" | "forget" | "set_guardrail" | "reset_guardrails" | "check_policy" | "governance_status" | "kill_switch" | "scan_context"\`\n- Strict /v1 Organization Brief: \`get_organization({ company_context: { required: false } })\` returns accepted workspace policy facts + learned defaults in \`company_context.packet\`\n- Dry-Run Policy Simulator: \`POST ${config.baseUrl}/api/v1/guardrails/check\` (\`{ "tool_slug": "...", "input": { ... } }\`) or MCP \`get_run_context({ action: "check_policy", tool_slug, input })\`\n- Guardrails & Policy Brief: \`GET/POST ${config.baseUrl}/api/v1/guardrails\`, \`POST ${config.baseUrl}/api/v1/guardrails/reset\`, \`GET ${config.baseUrl}/api/v1/governance\` (includes \`llmPolicyBrief\`)\n- MCP Resources: \`muxo://policy/brief\`, \`muxo://governance/policy\`, \`muxo://context\`, \`muxo://memory/graph\`\n- MCP Prompts: \`context\`, \`policy_guardrails\`, \`governed_execution\`, \`security_audit\`\n`);
|
|
810
831
|
return;
|
|
811
832
|
}
|
|
812
|
-
// Serve Installable Muxo Plugin Manifests & Metadata (Portable OpenAI Agent Plugins + Codex + Claude)
|
|
833
|
+
// Serve Installable Muxo Plugin Manifests & Metadata (Portable OpenAI Agent Plugins + Codex + Claude + Cursor)
|
|
813
834
|
const pluginJsonRoutes = {
|
|
814
835
|
"/.agents/plugins/marketplace.json": path.join(ROOT_DIR, ".agents", "plugins", "marketplace.json"),
|
|
815
836
|
"/.claude-plugin/marketplace.json": path.join(ROOT_DIR, ".claude-plugin", "marketplace.json"),
|
|
@@ -817,6 +838,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
817
838
|
"/plugins/muxo/mcp.json": path.join(ROOT_DIR, "plugins", "muxo", "mcp.json"),
|
|
818
839
|
"/plugins/muxo/.codex-plugin/plugin.json": path.join(ROOT_DIR, "plugins", "muxo", ".codex-plugin", "plugin.json"),
|
|
819
840
|
"/plugins/muxo/.claude-plugin/plugin.json": path.join(ROOT_DIR, "plugins", "muxo", ".claude-plugin", "plugin.json"),
|
|
841
|
+
"/plugins/muxo/.cursor-plugin/plugin.json": path.join(ROOT_DIR, "plugins", "muxo", ".cursor-plugin", "plugin.json"),
|
|
820
842
|
"/plugins/muxo/.app.json": path.join(ROOT_DIR, "plugins", "muxo", ".app.json"),
|
|
821
843
|
"/plugins/muxo/.mcp.json": path.join(ROOT_DIR, "plugins", "muxo", ".mcp.json"),
|
|
822
844
|
};
|
|
@@ -844,6 +866,10 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
844
866
|
file: path.join(ROOT_DIR, "plugins", "muxo", "skills", "setup", "SKILL.md"),
|
|
845
867
|
contentType: "text/markdown; charset=utf-8",
|
|
846
868
|
},
|
|
869
|
+
"/plugins/muxo/skills/muxo-activity-summary/SKILL.md": {
|
|
870
|
+
file: path.join(ROOT_DIR, "plugins", "muxo", "skills", "muxo-activity-summary", "SKILL.md"),
|
|
871
|
+
contentType: "text/markdown; charset=utf-8",
|
|
872
|
+
},
|
|
847
873
|
"/.codex/config.toml": {
|
|
848
874
|
file: path.join(ROOT_DIR, ".codex", "config.toml"),
|
|
849
875
|
contentType: "text/plain; charset=utf-8",
|
|
@@ -864,10 +890,15 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
864
890
|
const portableManifest = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, "plugins", "muxo", "plugin.json"), "utf8"));
|
|
865
891
|
const mcpConfig = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, "plugins", "muxo", "mcp.json"), "utf8"));
|
|
866
892
|
const codexManifest = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, "plugins", "muxo", ".codex-plugin", "plugin.json"), "utf8"));
|
|
893
|
+
const cursorManifestPath = path.join(ROOT_DIR, "plugins", "muxo", ".cursor-plugin", "plugin.json");
|
|
894
|
+
const cursorManifest = fs.existsSync(cursorManifestPath)
|
|
895
|
+
? JSON.parse(fs.readFileSync(cursorManifestPath, "utf8"))
|
|
896
|
+
: undefined;
|
|
867
897
|
sendJson(res, 200, {
|
|
868
898
|
ok: true,
|
|
869
899
|
plugin: portableManifest,
|
|
870
900
|
codexPlugin: codexManifest,
|
|
901
|
+
cursorPlugin: cursorManifest,
|
|
871
902
|
mcp: mcpConfig,
|
|
872
903
|
skills: getMuxoMcpSkills().map((s) => ({
|
|
873
904
|
name: s.name,
|
|
@@ -884,11 +915,14 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
884
915
|
portablePluginJson: `${config.baseUrl}/plugins/muxo/plugin.json`,
|
|
885
916
|
portableMcpJson: `${config.baseUrl}/plugins/muxo/mcp.json`,
|
|
886
917
|
onboardingSkill: `${config.baseUrl}/plugins/muxo/skills/setup/SKILL.md`,
|
|
918
|
+
activitySummarySkill: `${config.baseUrl}/plugins/muxo/skills/muxo-activity-summary/SKILL.md`,
|
|
887
919
|
codexPluginJson: `${config.baseUrl}/plugins/muxo/.codex-plugin/plugin.json`,
|
|
920
|
+
cursorPluginJson: `${config.baseUrl}/plugins/muxo/.cursor-plugin/plugin.json`,
|
|
888
921
|
codexConfigToml: `${config.baseUrl}/.codex/config.toml`,
|
|
889
922
|
},
|
|
890
923
|
installCommands: {
|
|
891
924
|
codex: "codex plugin add muxo@muxo",
|
|
925
|
+
cursor: "/add-plugin muxo",
|
|
892
926
|
cliSignup: "muxo login --signup --name my-agent",
|
|
893
927
|
oauthHandoff: "muxo link github --wait",
|
|
894
928
|
},
|
|
@@ -915,6 +949,35 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
915
949
|
res.end(renderLandingHtml());
|
|
916
950
|
return;
|
|
917
951
|
}
|
|
952
|
+
// Public Stripe Projects Hosted Static Sites (GET /sites/:slug and GET /sites/:slug/*)
|
|
953
|
+
const siteMatch = pathname.match(/^\/sites\/([^/]+)(?:\/(.*))?$/);
|
|
954
|
+
if (siteMatch && req.method === "GET") {
|
|
955
|
+
const siteSlug = decodeURIComponent(siteMatch[1]).replace(/\/+$/, "");
|
|
956
|
+
const filePath = siteMatch[2] ? decodeURIComponent(siteMatch[2]) : undefined;
|
|
957
|
+
const htmlContent = engine.getHostedSiteHtml(siteSlug, filePath);
|
|
958
|
+
if (!htmlContent) {
|
|
959
|
+
sendJson(res, 404, {
|
|
960
|
+
ok: false,
|
|
961
|
+
code: "site_not_found",
|
|
962
|
+
error: `Hosted site '${siteSlug}' was not found.`,
|
|
963
|
+
});
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
const contentType = filePath && filePath.endsWith(".css")
|
|
967
|
+
? "text/css; charset=utf-8"
|
|
968
|
+
: filePath && filePath.endsWith(".js")
|
|
969
|
+
? "application/javascript; charset=utf-8"
|
|
970
|
+
: filePath && filePath.endsWith(".json")
|
|
971
|
+
? "application/json; charset=utf-8"
|
|
972
|
+
: "text/html; charset=utf-8";
|
|
973
|
+
res.writeHead(200, {
|
|
974
|
+
"Content-Type": contentType,
|
|
975
|
+
"Access-Control-Allow-Origin": "*",
|
|
976
|
+
"Cache-Control": "no-store",
|
|
977
|
+
});
|
|
978
|
+
res.end(htmlContent);
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
918
981
|
// Workspace claim link: read-only; accepting requires the invited person's verified sign-in.
|
|
919
982
|
if (pathname.startsWith("/claim/") && req.method === "GET") {
|
|
920
983
|
const claimSlug = decodeURIComponent(pathname.slice("/claim/".length));
|
|
@@ -1041,30 +1104,289 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1041
1104
|
return;
|
|
1042
1105
|
}
|
|
1043
1106
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
1044
|
-
res.end(
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1107
|
+
res.end(renderOAuthCallbackPageHtml({
|
|
1108
|
+
providerSlug: handoff.providerSlug,
|
|
1109
|
+
providerName: handoff.providerName,
|
|
1110
|
+
status: handoff.status,
|
|
1111
|
+
workspaceId: handoff.workspaceId,
|
|
1112
|
+
externalAccountId: handoff.externalAccountId,
|
|
1113
|
+
}));
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
// Public bare /oauth/connect or /connect redirector & zero-OAuth status handler
|
|
1117
|
+
if ((pathname === "/oauth/connect" || pathname === "/connect") && req.method === "GET") {
|
|
1118
|
+
const queryToken = (url.searchParams.get("token") ||
|
|
1119
|
+
url.searchParams.get("link_token") ||
|
|
1120
|
+
"").trim();
|
|
1121
|
+
const directRedirect = (url.searchParams.get("redirect_url") ||
|
|
1122
|
+
url.searchParams.get("url") ||
|
|
1123
|
+
url.searchParams.get("link") ||
|
|
1124
|
+
"").trim();
|
|
1125
|
+
const workspaceId = url.searchParams.get("workspace_id") ||
|
|
1126
|
+
url.searchParams.get("user_id") ||
|
|
1127
|
+
undefined;
|
|
1128
|
+
const rawProvider = url.searchParams.get("provider") ||
|
|
1129
|
+
url.searchParams.get("toolkit") ||
|
|
1130
|
+
"";
|
|
1131
|
+
const cleanProvider = normalizeStripeProjectsProviderSlug(rawProvider) ||
|
|
1132
|
+
rawProvider.trim().toLowerCase().replace(/^(app\.|resource\.|projects\.)/, "");
|
|
1133
|
+
const wantsJson = url.searchParams.get("format") === "json" ||
|
|
1134
|
+
String(req.headers.accept || "").includes("application/json");
|
|
1135
|
+
if (queryToken) {
|
|
1136
|
+
const targetUrl = queryToken.startsWith("s_")
|
|
1137
|
+
? `https://backend.composio.dev/api/v3/s/${encodeURIComponent(queryToken.slice(2))}`
|
|
1138
|
+
: `https://connect.composio.dev/link/${encodeURIComponent(queryToken)}`;
|
|
1139
|
+
if (wantsJson) {
|
|
1140
|
+
sendJson(res, 200, { ok: true, status: "pending", redirectUrl: targetUrl });
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
res.writeHead(302, { Location: targetUrl, "Cache-Control": "no-store" });
|
|
1144
|
+
res.end();
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
if (directRedirect &&
|
|
1148
|
+
/^https:\/\/(?:connect|backend)\.composio\.dev\//i.test(directRedirect)) {
|
|
1149
|
+
if (wantsJson) {
|
|
1150
|
+
sendJson(res, 200, { ok: true, status: "pending", redirectUrl: directRedirect });
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
res.writeHead(302, { Location: directRedirect, "Cache-Control": "no-store" });
|
|
1154
|
+
res.end();
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
if (cleanProvider && !isStripeProjectsExclusiveProvider(cleanProvider)) {
|
|
1158
|
+
const allWs = [
|
|
1159
|
+
workspaceId
|
|
1160
|
+
? store.getWorkspace(workspaceId)
|
|
1161
|
+
: store.ensureDefaultWorkspace().workspace,
|
|
1162
|
+
].filter(Boolean);
|
|
1163
|
+
for (const ws of allWs) {
|
|
1164
|
+
if (!ws)
|
|
1165
|
+
continue;
|
|
1166
|
+
const pending = store
|
|
1167
|
+
.listConnections(ws.id)
|
|
1168
|
+
.find((c) => c.providerSlug.toLowerCase() === cleanProvider &&
|
|
1169
|
+
c.redirectUrl &&
|
|
1170
|
+
!isStripeProjectsExclusiveProvider(c.providerSlug));
|
|
1171
|
+
if (pending?.redirectUrl) {
|
|
1172
|
+
const nativeUrl = wrapMuxoConnectUrl(pending.redirectUrl);
|
|
1173
|
+
if (nativeUrl) {
|
|
1174
|
+
if (wantsJson) {
|
|
1175
|
+
sendJson(res, 200, {
|
|
1176
|
+
ok: true,
|
|
1177
|
+
provider: cleanProvider,
|
|
1178
|
+
status: pending.status,
|
|
1179
|
+
redirectUrl: nativeUrl,
|
|
1180
|
+
});
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
res.writeHead(302, { Location: nativeUrl, "Cache-Control": "no-store" });
|
|
1184
|
+
res.end();
|
|
1185
|
+
return;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
else if (!cleanProvider) {
|
|
1191
|
+
const allWs = [
|
|
1192
|
+
workspaceId
|
|
1193
|
+
? store.getWorkspace(workspaceId)
|
|
1194
|
+
: store.ensureDefaultWorkspace().workspace,
|
|
1195
|
+
].filter(Boolean);
|
|
1196
|
+
for (const ws of allWs) {
|
|
1197
|
+
if (!ws)
|
|
1198
|
+
continue;
|
|
1199
|
+
const pending = store
|
|
1200
|
+
.listConnections(ws.id)
|
|
1201
|
+
.find((c) => c.status === "pending" &&
|
|
1202
|
+
c.redirectUrl &&
|
|
1203
|
+
!isStripeProjectsExclusiveProvider(c.providerSlug));
|
|
1204
|
+
if (pending?.redirectUrl) {
|
|
1205
|
+
const nativeUrl = wrapMuxoConnectUrl(pending.redirectUrl);
|
|
1206
|
+
if (nativeUrl &&
|
|
1207
|
+
(/^https:\/\/(?:connect|backend)\.composio\.dev\//i.test(nativeUrl) ||
|
|
1208
|
+
nativeUrl.includes("/oauth/connect/"))) {
|
|
1209
|
+
if (wantsJson) {
|
|
1210
|
+
sendJson(res, 200, {
|
|
1211
|
+
ok: true,
|
|
1212
|
+
provider: pending.providerSlug,
|
|
1213
|
+
status: "pending",
|
|
1214
|
+
redirectUrl: nativeUrl,
|
|
1215
|
+
});
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
res.writeHead(302, { Location: nativeUrl, "Cache-Control": "no-store" });
|
|
1219
|
+
res.end();
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
const displayProviderSlug = cleanProvider || "supabase";
|
|
1226
|
+
const displayProviderName = cleanProvider
|
|
1227
|
+
? cleanProvider.charAt(0).toUpperCase() + cleanProvider.slice(1)
|
|
1228
|
+
: "Supabase & Vercel (Stripe Projects)";
|
|
1229
|
+
if (wantsJson) {
|
|
1230
|
+
sendJson(res, 200, {
|
|
1231
|
+
ok: true,
|
|
1232
|
+
status: "active",
|
|
1233
|
+
lane: "stripe_projects",
|
|
1234
|
+
provider: displayProviderSlug,
|
|
1235
|
+
noOAuthRequired: true,
|
|
1236
|
+
redirectUrl: null,
|
|
1237
|
+
message: "Provisioned Stripe Projects cloud resources (Supabase, Vercel, Cloudflare, Turso, Neon, etc.) are active immediately with zero OAuth required.",
|
|
1238
|
+
});
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
res.writeHead(200, {
|
|
1242
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
1243
|
+
"Cache-Control": "no-store",
|
|
1244
|
+
});
|
|
1245
|
+
res.end(renderOAuthCallbackPageHtml({
|
|
1246
|
+
providerSlug: displayProviderSlug,
|
|
1247
|
+
providerName: displayProviderName,
|
|
1248
|
+
status: "active",
|
|
1249
|
+
workspaceId: workspaceId || "default",
|
|
1250
|
+
externalAccountId: `sp_conn_${displayProviderSlug}`,
|
|
1251
|
+
}));
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
// Muxo OAuth Connection Handoff (GET/POST /oauth/connect/:token or /connect/:token)
|
|
1255
|
+
const connectPageMatch = pathname.match(/^\/(?:oauth\/)?connect\/([^/]+)$/);
|
|
1256
|
+
if (connectPageMatch && (req.method === "GET" || req.method === "POST")) {
|
|
1257
|
+
const token = decodeURIComponent(connectPageMatch[1]);
|
|
1258
|
+
const workspaceId = url.searchParams.get("workspace_id") ||
|
|
1259
|
+
url.searchParams.get("user_id") ||
|
|
1260
|
+
undefined;
|
|
1261
|
+
const providerSlug = url.searchParams.get("provider") ||
|
|
1262
|
+
url.searchParams.get("toolkit") ||
|
|
1263
|
+
undefined;
|
|
1264
|
+
const callbackUrl = url.searchParams.get("callback_url") || undefined;
|
|
1265
|
+
const connectedAccountId = url.searchParams.get("connected_account_id") || undefined;
|
|
1266
|
+
if (req.method === "GET") {
|
|
1267
|
+
const wantsJson = url.searchParams.get("format") === "json" ||
|
|
1268
|
+
String(req.headers.accept || "").includes("application/json");
|
|
1269
|
+
const isTestToken = token.startsWith("lk_test");
|
|
1270
|
+
const forceHtml = url.searchParams.get("render") === "html";
|
|
1271
|
+
// Redirect real Composio link tokens directly to Composio's native hosted OAuth page
|
|
1272
|
+
if (!wantsJson && !isTestToken && !forceHtml && (token.startsWith("lk_") || token.startsWith("s_"))) {
|
|
1273
|
+
const nativeComposioUrl = token.startsWith("s_")
|
|
1274
|
+
? `https://backend.composio.dev/api/v3/s/${encodeURIComponent(token.slice(2))}`
|
|
1275
|
+
: `https://connect.composio.dev/link/${encodeURIComponent(token)}`;
|
|
1276
|
+
res.writeHead(302, {
|
|
1277
|
+
Location: nativeComposioUrl,
|
|
1278
|
+
"Cache-Control": "no-store",
|
|
1279
|
+
});
|
|
1280
|
+
res.end();
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
const meta = await fetchMuxoConnectLinkMetadata(token, {
|
|
1284
|
+
workspaceId,
|
|
1285
|
+
providerSlug,
|
|
1286
|
+
callbackUrl,
|
|
1287
|
+
connectedAccountId,
|
|
1288
|
+
});
|
|
1289
|
+
if (url.searchParams.get("continue") === "1" ||
|
|
1290
|
+
url.searchParams.get("auto") === "1") {
|
|
1291
|
+
const submitted = await submitMuxoConnectLink(token, {}, {
|
|
1292
|
+
workspaceId,
|
|
1293
|
+
providerSlug: meta.toolkitSlug,
|
|
1294
|
+
callbackUrl: meta.callbackUrl || callbackUrl,
|
|
1295
|
+
connectedAccountId: meta.connectedAccountId,
|
|
1296
|
+
});
|
|
1297
|
+
if (submitted.ok && submitted.redirectUrl) {
|
|
1298
|
+
res.writeHead(302, { Location: submitted.redirectUrl });
|
|
1299
|
+
res.end();
|
|
1300
|
+
return;
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
if (wantsJson) {
|
|
1304
|
+
sendJson(res, 200, { ok: true, handoff: meta });
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
res.writeHead(200, {
|
|
1308
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
1309
|
+
"Cache-Control": "no-store",
|
|
1310
|
+
"Referrer-Policy": "no-referrer",
|
|
1311
|
+
});
|
|
1312
|
+
res.end(renderOAuthConnectPageHtml({
|
|
1313
|
+
token: meta.token,
|
|
1314
|
+
toolkitSlug: meta.toolkitSlug,
|
|
1315
|
+
toolkitName: meta.toolkitName,
|
|
1316
|
+
workspaceId,
|
|
1317
|
+
authMode: meta.authMode,
|
|
1318
|
+
status: meta.status,
|
|
1319
|
+
callbackUrl: meta.callbackUrl || callbackUrl,
|
|
1320
|
+
expectedInputFields: meta.expectedInputFields,
|
|
1321
|
+
}));
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
// POST /oauth/connect/:token
|
|
1325
|
+
const rawBody = await readBody(req);
|
|
1326
|
+
let input = {};
|
|
1327
|
+
const contentType = String(req.headers["content-type"] || "");
|
|
1328
|
+
if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
1329
|
+
const formParams = new URLSearchParams(rawBody);
|
|
1330
|
+
for (const [k, v] of formParams.entries()) {
|
|
1331
|
+
if (v !== "")
|
|
1332
|
+
input[k] = v;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
else if (rawBody) {
|
|
1336
|
+
try {
|
|
1337
|
+
const parsed = JSON.parse(rawBody);
|
|
1338
|
+
input =
|
|
1339
|
+
parsed && typeof parsed.input === "object" && parsed.input !== null
|
|
1340
|
+
? parsed.input
|
|
1341
|
+
: parsed;
|
|
1342
|
+
}
|
|
1343
|
+
catch {
|
|
1344
|
+
input = {};
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
const submitted = await submitMuxoConnectLink(token, input, {
|
|
1348
|
+
workspaceId,
|
|
1349
|
+
providerSlug,
|
|
1350
|
+
callbackUrl,
|
|
1351
|
+
connectedAccountId,
|
|
1352
|
+
});
|
|
1353
|
+
const wantsJson = url.searchParams.get("format") === "json" ||
|
|
1354
|
+
String(req.headers.accept || "").includes("application/json") ||
|
|
1355
|
+
contentType.includes("application/json");
|
|
1356
|
+
if (wantsJson) {
|
|
1357
|
+
sendJson(res, submitted.ok ? 200 : 400, {
|
|
1358
|
+
ok: submitted.ok,
|
|
1359
|
+
status: submitted.status,
|
|
1360
|
+
redirectUrl: submitted.redirectUrl,
|
|
1361
|
+
redirect_url: submitted.redirectUrl,
|
|
1362
|
+
connectedAccountId: submitted.connectedAccountId,
|
|
1363
|
+
...(submitted.error ? { error: submitted.error } : {}),
|
|
1364
|
+
});
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
if (submitted.ok && submitted.redirectUrl) {
|
|
1368
|
+
res.writeHead(302, { Location: submitted.redirectUrl });
|
|
1369
|
+
res.end();
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const meta = await fetchMuxoConnectLinkMetadata(token, {
|
|
1373
|
+
workspaceId,
|
|
1374
|
+
providerSlug,
|
|
1375
|
+
callbackUrl,
|
|
1376
|
+
connectedAccountId,
|
|
1377
|
+
});
|
|
1378
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
1379
|
+
res.end(renderOAuthConnectPageHtml({
|
|
1380
|
+
token: meta.token,
|
|
1381
|
+
toolkitSlug: meta.toolkitSlug,
|
|
1382
|
+
toolkitName: meta.toolkitName,
|
|
1383
|
+
workspaceId,
|
|
1384
|
+
authMode: meta.authMode,
|
|
1385
|
+
status: meta.status,
|
|
1386
|
+
callbackUrl: meta.callbackUrl || callbackUrl,
|
|
1387
|
+
expectedInputFields: meta.expectedInputFields,
|
|
1388
|
+
error: submitted.error || "Could not start authorization handoff.",
|
|
1389
|
+
}));
|
|
1068
1390
|
return;
|
|
1069
1391
|
}
|
|
1070
1392
|
// One-click Plan Approval Handoff Page (GET /approve/:planId)
|
|
@@ -1151,8 +1473,9 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1151
1473
|
"Cache-Control": "no-cache",
|
|
1152
1474
|
Connection: "keep-alive",
|
|
1153
1475
|
"Mcp-Session-Id": sessionId,
|
|
1476
|
+
"MCP-Protocol-Version": LATEST_MCP_PROTOCOL_VERSION,
|
|
1154
1477
|
"Access-Control-Allow-Origin": "*",
|
|
1155
|
-
"Access-Control-Expose-Headers": "Mcp-Session-Id",
|
|
1478
|
+
"Access-Control-Expose-Headers": "Mcp-Session-Id, MCP-Protocol-Version",
|
|
1156
1479
|
});
|
|
1157
1480
|
if (req.method !== "HEAD") {
|
|
1158
1481
|
res.write(`event: endpoint\ndata: ${JSON.stringify({ uri: `${config.baseUrl}/mcp`, sessionId })}\n\n`);
|
|
@@ -1163,15 +1486,20 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1163
1486
|
sendJson(res, 200, {
|
|
1164
1487
|
ok: true,
|
|
1165
1488
|
transport: "streamable-http",
|
|
1166
|
-
protocolVersion:
|
|
1489
|
+
protocolVersion: LATEST_MCP_PROTOCOL_VERSION,
|
|
1490
|
+
supportedVersions: [...SUPPORTED_MCP_PROTOCOL_VERSIONS],
|
|
1167
1491
|
serverInfo: {
|
|
1168
1492
|
name: "muxo-mcp-server",
|
|
1493
|
+
title: "Muxo Unified Agent Runtime & Governance MCP Server",
|
|
1169
1494
|
version: MUXO_VERSION,
|
|
1170
1495
|
},
|
|
1171
1496
|
instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
|
|
1172
1497
|
toolsCount: MUXO_MCP_TOOLS.length,
|
|
1173
1498
|
skillsCount: getMuxoMcpSkills().length,
|
|
1174
|
-
}, {
|
|
1499
|
+
}, {
|
|
1500
|
+
"Mcp-Session-Id": sessionId,
|
|
1501
|
+
"MCP-Protocol-Version": LATEST_MCP_PROTOCOL_VERSION,
|
|
1502
|
+
});
|
|
1175
1503
|
return;
|
|
1176
1504
|
}
|
|
1177
1505
|
if (pathname === "/mcp" && req.method === "DELETE") {
|
|
@@ -1184,15 +1512,39 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1184
1512
|
if (pathname === "/mcp" && req.method === "POST") {
|
|
1185
1513
|
const raw = await readBody(req);
|
|
1186
1514
|
const parsed = JSON.parse(raw || "{}");
|
|
1515
|
+
const headerCheck = !Array.isArray(parsed)
|
|
1516
|
+
? validateMcpHttpHeadersAndMeta({
|
|
1517
|
+
protocolVersion: String(req.headers["mcp-protocol-version"] || ""),
|
|
1518
|
+
method: String(req.headers["mcp-method"] || ""),
|
|
1519
|
+
name: String(req.headers["mcp-name"] || ""),
|
|
1520
|
+
}, parsed)
|
|
1521
|
+
: { ok: true, protocolVersion: LATEST_MCP_PROTOCOL_VERSION };
|
|
1522
|
+
if (!headerCheck.ok) {
|
|
1523
|
+
const fallbackSessionId = String(req.headers["mcp-session-id"] || "") ||
|
|
1524
|
+
`mcp_sess_${crypto.randomBytes(6).toString("hex")}`;
|
|
1525
|
+
sendJson(res, 200, {
|
|
1526
|
+
jsonrpc: "2.0",
|
|
1527
|
+
id: headerCheck.id,
|
|
1528
|
+
error: headerCheck.error,
|
|
1529
|
+
}, {
|
|
1530
|
+
"Mcp-Session-Id": fallbackSessionId,
|
|
1531
|
+
"MCP-Protocol-Version": LATEST_MCP_PROTOCOL_VERSION,
|
|
1532
|
+
});
|
|
1533
|
+
return;
|
|
1534
|
+
}
|
|
1187
1535
|
const resolved = await requireContext(req, res, pathname, !Array.isArray(parsed) && isPublicMcpRequest(parsed));
|
|
1188
1536
|
if (!resolved)
|
|
1189
1537
|
return;
|
|
1190
1538
|
const { ctx } = resolved;
|
|
1191
1539
|
const sessionId = String(req.headers["mcp-session-id"] || "") ||
|
|
1192
1540
|
`mcp_sess_${ctx.workspace.id}`;
|
|
1541
|
+
const responseHeaders = {
|
|
1542
|
+
"Mcp-Session-Id": sessionId,
|
|
1543
|
+
"MCP-Protocol-Version": headerCheck.protocolVersion,
|
|
1544
|
+
};
|
|
1193
1545
|
if (Array.isArray(parsed)) {
|
|
1194
1546
|
const responses = (await Promise.all(parsed.map((item) => handleJsonRpcMcpRequest(engine, ctx, (item || {}))))).filter((r) => r !== null);
|
|
1195
|
-
sendJson(res, 200, responses,
|
|
1547
|
+
sendJson(res, 200, responses, responseHeaders);
|
|
1196
1548
|
return;
|
|
1197
1549
|
}
|
|
1198
1550
|
const rpc = parsed;
|
|
@@ -1200,12 +1552,12 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1200
1552
|
if (!response) {
|
|
1201
1553
|
res.writeHead(202, {
|
|
1202
1554
|
"Access-Control-Allow-Origin": "*",
|
|
1203
|
-
|
|
1555
|
+
...responseHeaders,
|
|
1204
1556
|
});
|
|
1205
1557
|
res.end();
|
|
1206
1558
|
return;
|
|
1207
1559
|
}
|
|
1208
|
-
sendJson(res, 200, response,
|
|
1560
|
+
sendJson(res, 200, response, responseHeaders);
|
|
1209
1561
|
return;
|
|
1210
1562
|
}
|
|
1211
1563
|
// Auth Endpoints
|
|
@@ -1263,7 +1615,11 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1263
1615
|
const isPublicRoute = ((pathname === "/api/v1/tools" || pathname === "/v1/tools/search") &&
|
|
1264
1616
|
(req.method === "GET" || req.method === "POST")) ||
|
|
1265
1617
|
(pathname.startsWith("/api/v1/tools/") && req.method === "GET") ||
|
|
1266
|
-
(pathname === "/api/v1/billing/webhook" && req.method === "POST")
|
|
1618
|
+
(pathname === "/api/v1/billing/webhook" && req.method === "POST") ||
|
|
1619
|
+
((pathname === "/webhooks" ||
|
|
1620
|
+
pathname === "/webhooks/composio" ||
|
|
1621
|
+
pathname === "/api/v1/triggers/webhook") &&
|
|
1622
|
+
req.method === "POST");
|
|
1267
1623
|
const resolvedCtx = await requireContext(req, res, pathname, isPublicRoute);
|
|
1268
1624
|
if (!resolvedCtx)
|
|
1269
1625
|
return;
|
|
@@ -1277,6 +1633,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1277
1633
|
profile: engine.getProfile(ctx),
|
|
1278
1634
|
organization: org,
|
|
1279
1635
|
governance: engine.governance.getStatus(),
|
|
1636
|
+
llmPolicyBrief: guardrailsSummary.llmPolicyBrief,
|
|
1280
1637
|
plainEnglishGuardrails: guardrailsSummary.plainEnglish,
|
|
1281
1638
|
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
1282
1639
|
activeGuardrails: guardrailsSummary.activeGuardrails,
|
|
@@ -1451,6 +1808,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1451
1808
|
auditChain: fullReport.auditChain,
|
|
1452
1809
|
},
|
|
1453
1810
|
workspacePolicy: ws.policy,
|
|
1811
|
+
llmPolicyBrief: guardrailsSummary.llmPolicyBrief,
|
|
1454
1812
|
plainEnglishGuardrails: guardrailsSummary.plainEnglish,
|
|
1455
1813
|
activeMemoryDefaults: guardrailsSummary.activeDefaults,
|
|
1456
1814
|
activeGuardrails: guardrailsSummary.activeGuardrails,
|
|
@@ -1465,16 +1823,37 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
1465
1823
|
});
|
|
1466
1824
|
return;
|
|
1467
1825
|
}
|
|
1826
|
+
if ((pathname === "/api/v1/guardrails/reset" || pathname === "/api/v1/policy/reset") &&
|
|
1827
|
+
req.method === "POST") {
|
|
1828
|
+
const resetResult = await engine.resetWorkspaceGuardrails(ctx);
|
|
1829
|
+
sendJson(res, 200, {
|
|
1830
|
+
...resetResult,
|
|
1831
|
+
humanMessage: "Reset workspace guardrails to Safe Default.",
|
|
1832
|
+
});
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
if ((pathname === "/api/v1/guardrails/check" ||
|
|
1836
|
+
pathname === "/api/v1/guardrails/simulate" ||
|
|
1837
|
+
pathname === "/api/v1/policy/check") &&
|
|
1838
|
+
req.method === "POST") {
|
|
1839
|
+
const body = JSON.parse((await readBody(req)) || "{}");
|
|
1840
|
+
const toolSlug = String(body.tool_slug || body.toolSlug || body.slug || body.tool_id || "instant.web.search").trim();
|
|
1841
|
+
const input = (body.input || body.arguments) || {};
|
|
1842
|
+
const result = await engine.checkToolPolicy(ctx, { toolSlug, input });
|
|
1843
|
+
const summary = engine.getGuardrailsSummary(ctx);
|
|
1844
|
+
sendJson(res, 200, {
|
|
1845
|
+
...result,
|
|
1846
|
+
simulation: result,
|
|
1847
|
+
llmPolicyBrief: summary.llmPolicyBrief,
|
|
1848
|
+
});
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1468
1851
|
if (pathname === "/api/v1/guardrails" && req.method === "POST") {
|
|
1469
1852
|
const body = JSON.parse((await readBody(req)) || "{}");
|
|
1470
1853
|
if (body.reset === true) {
|
|
1471
|
-
const
|
|
1472
|
-
const plainEnglish = describePolicyInPlainEnglish(updated.policy);
|
|
1854
|
+
const resetResult = await engine.resetWorkspaceGuardrails(ctx);
|
|
1473
1855
|
sendJson(res, 200, {
|
|
1474
|
-
|
|
1475
|
-
workspace: updated,
|
|
1476
|
-
policy: updated.policy,
|
|
1477
|
-
plainEnglish,
|
|
1856
|
+
...resetResult,
|
|
1478
1857
|
humanMessage: "Reset workspace guardrails to Safe Default.",
|
|
1479
1858
|
});
|
|
1480
1859
|
return;
|
|
@@ -2968,52 +3347,143 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
2968
3347
|
}
|
|
2969
3348
|
if (pathname === "/api/v1/triggers" && req.method === "GET") {
|
|
2970
3349
|
const provider = url.searchParams.get("provider") || undefined;
|
|
3350
|
+
const triggerSlug = url.searchParams.get("triggerSlug") || url.searchParams.get("trigger_slug") || undefined;
|
|
3351
|
+
const composioByok = store.getByokKey(ctx.workspace.id, "composio");
|
|
2971
3352
|
const available = engine.composioSdk.listTriggerTypes(provider);
|
|
2972
3353
|
const subscriptions = engine.composioSdk.listActiveTriggers(ctx.workspace.id);
|
|
2973
3354
|
const events = engine.composioSdk.listTriggerEvents(ctx.workspace.id, 25);
|
|
3355
|
+
const triggerType = triggerSlug
|
|
3356
|
+
? await engine.composioSdk.getTriggerType(triggerSlug, composioByok)
|
|
3357
|
+
: undefined;
|
|
2974
3358
|
sendJson(res, 200, {
|
|
2975
3359
|
ok: true,
|
|
2976
3360
|
availableTriggers: available,
|
|
2977
3361
|
activeSubscriptions: subscriptions,
|
|
2978
3362
|
recentEvents: events,
|
|
3363
|
+
webhookConfig: engine.composioSdk.getWebhookConfigInfo(),
|
|
3364
|
+
...(triggerType ? { triggerType } : {}),
|
|
2979
3365
|
});
|
|
2980
3366
|
return;
|
|
2981
3367
|
}
|
|
2982
3368
|
if (pathname === "/api/v1/triggers" && req.method === "POST") {
|
|
2983
3369
|
const composioByok = store.getByokKey(ctx.workspace.id, "composio");
|
|
2984
3370
|
const body = JSON.parse((await readBody(req)) || "{}");
|
|
3371
|
+
const rawConfig = body.triggerConfig && typeof body.triggerConfig === "object"
|
|
3372
|
+
? body.triggerConfig
|
|
3373
|
+
: body.config && typeof body.config === "object"
|
|
3374
|
+
? body.config
|
|
3375
|
+
: {};
|
|
2985
3376
|
const subscription = await engine.composioSdk.enableTrigger({
|
|
2986
3377
|
workspaceId: ctx.workspace.id,
|
|
3378
|
+
userId: body.userId ? String(body.userId) : undefined,
|
|
2987
3379
|
triggerSlug: String(body.triggerSlug || body.trigger_slug || "GITHUB_COMMIT_EVENT"),
|
|
2988
3380
|
providerSlug: String(body.providerSlug || body.provider || "github"),
|
|
2989
3381
|
connectedAccountId: body.connectedAccountId
|
|
2990
3382
|
? String(body.connectedAccountId)
|
|
2991
3383
|
: undefined,
|
|
2992
3384
|
webhookUrl: body.webhookUrl ? String(body.webhookUrl) : undefined,
|
|
2993
|
-
config:
|
|
3385
|
+
config: rawConfig,
|
|
3386
|
+
subscribe: body.subscribe === true,
|
|
2994
3387
|
composioApiKey: composioByok,
|
|
2995
3388
|
});
|
|
2996
3389
|
sendJson(res, 201, { ok: true, subscription });
|
|
2997
3390
|
return;
|
|
2998
3391
|
}
|
|
2999
|
-
if (pathname === "/api/v1/triggers/webhook"
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
const
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3392
|
+
if ((pathname === "/api/v1/triggers/webhook" ||
|
|
3393
|
+
pathname === "/webhooks" ||
|
|
3394
|
+
pathname === "/webhooks/composio") &&
|
|
3395
|
+
req.method === "POST") {
|
|
3396
|
+
const rawBody = await readBody(req);
|
|
3397
|
+
const sigHeader = String(req.headers["webhook-signature"] ||
|
|
3398
|
+
req.headers["x-composio-webhook-signature"] ||
|
|
3399
|
+
"").trim();
|
|
3400
|
+
const webhookIdHeader = String(req.headers["webhook-id"] || req.headers["x-composio-webhook-id"] || "").trim();
|
|
3401
|
+
const webhookTimestampHeader = String(req.headers["webhook-timestamp"] ||
|
|
3402
|
+
req.headers["x-composio-webhook-timestamp"] ||
|
|
3403
|
+
"").trim();
|
|
3404
|
+
const webhookVersionHeader = String(req.headers["x-composio-webhook-version"] || "").trim();
|
|
3405
|
+
let signatureVerified = false;
|
|
3406
|
+
if (sigHeader) {
|
|
3407
|
+
const check = await verifyComposioWebhookSignature({
|
|
3408
|
+
rawPayload: rawBody,
|
|
3409
|
+
signatureHeader: sigHeader,
|
|
3410
|
+
webhookIdHeader,
|
|
3411
|
+
webhookTimestampHeader,
|
|
3412
|
+
secret: config.composioWebhookSecret,
|
|
3413
|
+
});
|
|
3414
|
+
if (config.composioWebhookSecret && !check.verified) {
|
|
3415
|
+
sendJson(res, 401, {
|
|
3416
|
+
ok: false,
|
|
3417
|
+
error: `Invalid Composio webhook signature (${check.reason || "verification_failed"})`,
|
|
3418
|
+
});
|
|
3419
|
+
return;
|
|
3420
|
+
}
|
|
3421
|
+
signatureVerified = check.verified;
|
|
3422
|
+
}
|
|
3423
|
+
const body = JSON.parse(rawBody || "{}");
|
|
3424
|
+
const envelope = parseComposioWebhookEnvelope(body, {
|
|
3425
|
+
webhookId: webhookIdHeader || undefined,
|
|
3426
|
+
webhookTimestamp: webhookTimestampHeader || undefined,
|
|
3427
|
+
webhookVersion: webhookVersionHeader || undefined,
|
|
3428
|
+
});
|
|
3429
|
+
let targetWorkspaceId = ctx.workspace.id;
|
|
3430
|
+
if (envelope.userId && store.getWorkspace(envelope.userId)) {
|
|
3431
|
+
targetWorkspaceId = envelope.userId;
|
|
3432
|
+
}
|
|
3433
|
+
if (envelope.eventType === "composio.connected_account.activated" ||
|
|
3434
|
+
envelope.eventType === "composio.connected_account.expired") {
|
|
3435
|
+
const nextStatus = envelope.eventType === "composio.connected_account.activated"
|
|
3436
|
+
? "active"
|
|
3437
|
+
: "expired";
|
|
3438
|
+
store.upsertConnection({
|
|
3439
|
+
workspaceId: targetWorkspaceId,
|
|
3440
|
+
lane: "composio_sdk",
|
|
3441
|
+
providerSlug: envelope.providerSlug,
|
|
3442
|
+
providerName: envelope.providerSlug.charAt(0).toUpperCase() +
|
|
3443
|
+
envelope.providerSlug.slice(1),
|
|
3444
|
+
status: nextStatus,
|
|
3445
|
+
externalAccountId: envelope.connectedAccountId || null,
|
|
3446
|
+
metadata: {
|
|
3447
|
+
webhookEvent: envelope.eventType,
|
|
3448
|
+
webhookId: envelope.webhookId,
|
|
3449
|
+
updatedViaWebhookAt: new Date().toISOString(),
|
|
3450
|
+
},
|
|
3451
|
+
});
|
|
3452
|
+
if (nextStatus === "expired") {
|
|
3453
|
+
engine.composioSdk.disableTriggersMatching({
|
|
3454
|
+
workspaceId: targetWorkspaceId,
|
|
3455
|
+
connectedAccountId: envelope.connectedAccountId,
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
else if (envelope.eventType === "composio.trigger.disabled") {
|
|
3460
|
+
engine.composioSdk.disableTriggersMatching({
|
|
3461
|
+
workspaceId: targetWorkspaceId,
|
|
3462
|
+
triggerId: envelope.triggerId,
|
|
3463
|
+
triggerSlug: envelope.triggerSlug,
|
|
3464
|
+
connectedAccountId: envelope.connectedAccountId,
|
|
3465
|
+
});
|
|
3466
|
+
}
|
|
3467
|
+
const rawPayloadText = JSON.stringify(envelope.payloadObj);
|
|
3007
3468
|
const injectionScan = engine.governance.scanMemoryContent({
|
|
3008
|
-
workspaceId:
|
|
3469
|
+
workspaceId: targetWorkspaceId,
|
|
3009
3470
|
content: rawPayloadText,
|
|
3010
3471
|
});
|
|
3011
3472
|
const disposition = injectionScan.safe ? "dispatched" : "quarantined";
|
|
3012
3473
|
const eventRecord = {
|
|
3013
3474
|
id: `trgevt_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`,
|
|
3014
|
-
workspaceId:
|
|
3015
|
-
|
|
3016
|
-
|
|
3475
|
+
workspaceId: targetWorkspaceId,
|
|
3476
|
+
eventType: envelope.eventType,
|
|
3477
|
+
webhookVersion: envelope.webhookVersion,
|
|
3478
|
+
signatureVerified,
|
|
3479
|
+
webhookId: envelope.webhookId,
|
|
3480
|
+
triggerId: envelope.triggerId,
|
|
3481
|
+
triggerSlug: envelope.triggerSlug,
|
|
3482
|
+
providerSlug: envelope.providerSlug,
|
|
3483
|
+
connectedAccountId: envelope.connectedAccountId,
|
|
3484
|
+
userId: envelope.userId,
|
|
3485
|
+
projectId: envelope.projectId,
|
|
3486
|
+
orgId: envelope.orgId,
|
|
3017
3487
|
receivedAt: new Date().toISOString(),
|
|
3018
3488
|
promptInjectionScan: {
|
|
3019
3489
|
safe: injectionScan.safe,
|
|
@@ -3021,7 +3491,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
|
|
|
3021
3491
|
findings: injectionScan.findings.map((f) => `${f.patternId}: ${f.description}`),
|
|
3022
3492
|
},
|
|
3023
3493
|
disposition: disposition,
|
|
3024
|
-
payloadPreview: payloadObj,
|
|
3494
|
+
payloadPreview: envelope.payloadObj,
|
|
3025
3495
|
};
|
|
3026
3496
|
engine.composioSdk.recordTriggerEvent(eventRecord);
|
|
3027
3497
|
sendJson(res, injectionScan.safe ? 200 : 202, {
|