@muxoai/platform 1.3.0 → 1.3.1

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.
Files changed (36) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude-plugin/marketplace.json +19 -4
  3. package/.codex/config.toml +9 -2
  4. package/README.md +54 -19
  5. package/dist/cli.js +780 -94
  6. package/dist/engine.d.ts +1 -0
  7. package/dist/engine.js +52 -4
  8. package/dist/mcp.d.ts +6 -1
  9. package/dist/mcp.js +108 -22
  10. package/dist/nl.js +60 -36
  11. package/dist/providers/composio-instant.d.ts +1 -0
  12. package/dist/providers/composio-instant.js +6 -5
  13. package/dist/providers/composio-sdk.d.ts +1 -0
  14. package/dist/providers/composio-sdk.js +6 -2
  15. package/dist/providers/stripe-billing.js +14 -4
  16. package/dist/providers/stripe-projects.js +61 -4
  17. package/dist/providers/supermemory.d.ts +2 -0
  18. package/dist/providers/supermemory.js +58 -40
  19. package/dist/server.js +85 -27
  20. package/dist/store.d.ts +1 -0
  21. package/dist/store.js +20 -4
  22. package/dist/ui/console-html.js +134 -114
  23. package/package.json +1 -1
  24. package/plugins/muxo/.app.json +1 -1
  25. package/plugins/muxo/.claude-plugin/plugin.json +2 -2
  26. package/plugins/muxo/.codex-plugin/plugin.json +7 -5
  27. package/plugins/muxo/.mcp.json +1 -33
  28. package/plugins/muxo/assets/logo.png +0 -0
  29. package/plugins/muxo/hooks/session-start.sh +1 -1
  30. package/plugins/muxo/plugin.json +63 -6
  31. package/plugins/muxo/skills/muxo/SKILL.md +92 -14
  32. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  33. package/plugins/muxo/skills/muxo/references/cli.md +4 -2
  34. package/plugins/muxo/skills/muxo/references/mcp.md +15 -9
  35. package/plugins/muxo/skills/setup/SKILL.md +8 -2
  36. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
@@ -192,10 +192,20 @@ export class StripeBillingAdapter {
192
192
  const prices = await this.ensurePrices(params.workspace.id);
193
193
  const form = new URLSearchParams();
194
194
  form.set("mode", params.kind === "starter_subscription" ? "subscription" : "payment");
195
- form.set("line_items[0][price]", params.kind === "starter_subscription"
196
- ? prices.starterPriceId
197
- : prices.creditsPriceId);
198
- form.set("line_items[0][quantity]", "1");
195
+ if (params.kind === "starter_subscription") {
196
+ form.set("line_items[0][price]", prices.starterPriceId);
197
+ form.set("line_items[0][quantity]", "1");
198
+ }
199
+ else if (creditsAmount === 1000) {
200
+ form.set("line_items[0][price]", prices.creditsPriceId);
201
+ form.set("line_items[0][quantity]", "1");
202
+ }
203
+ else {
204
+ form.set("line_items[0][price_data][currency]", "usd");
205
+ form.set("line_items[0][price_data][unit_amount]", String(Math.max(50, Math.round(creditsAmount))));
206
+ form.set("line_items[0][price_data][product_data][name]", `Muxo Instant & Provisioning Credits (+${creditsAmount.toLocaleString()} Credits)`);
207
+ form.set("line_items[0][quantity]", "1");
208
+ }
199
209
  form.set("client_reference_id", params.workspace.id);
200
210
  form.set("customer_email", params.customerEmail);
201
211
  form.set("metadata[workspace_id]", params.workspace.id);
@@ -37,7 +37,7 @@ export class StripeProjectsAdapter {
37
37
  slug: "resource.projects.rotate_credentials",
38
38
  upstreamSlug: "projects/rotate",
39
39
  name: "Cloud Infrastructure: Rotate Resource Credentials",
40
- description: "Rotate credentials for a provisioned resource via Stripe Projects and automatically sync the updated secrets into Muxo's encrypted workspace vault.",
40
+ description: "Rotate credentials for a provisioned resource via Muxo Cloud Infrastructure and automatically sync the updated secrets into Muxo's encrypted workspace vault.",
41
41
  lane: "stripe_projects",
42
42
  providerSlug: "projects",
43
43
  providerName: "Muxo Cloud Infrastructure",
@@ -249,9 +249,9 @@ export class StripeProjectsAdapter {
249
249
  if (this.tools.has(slug)) {
250
250
  return this.tools.get(slug);
251
251
  }
252
- // If given a 2-part slug like "resource.neon", or a 3-part alias like "resource.turso.create_database" / "resource.turso.provision"
252
+ // If given a 2-part slug like "resource.neon", or a 3-part alias like "resource.turso.create_database" / "projects.neon.create_database"
253
253
  const parts = slug.split(".");
254
- if (parts[0] === "resource" && parts.length >= 2) {
254
+ if ((parts[0] === "resource" || parts[0] === "projects") && parts.length >= 2) {
255
255
  const providerSlug = parts[1].toLowerCase();
256
256
  const actionPart = parts[2]?.toLowerCase() || "";
257
257
  if (parts.length === 2 ||
@@ -392,6 +392,12 @@ export class StripeProjectsAdapter {
392
392
  const k = trimmed.slice(0, eq).trim();
393
393
  const v = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
394
394
  parsed[k] = v;
395
+ if (k.endsWith("_TURSO_DATABASE_URL") && !parsed.TURSO_DATABASE_URL) {
396
+ parsed.TURSO_DATABASE_URL = v;
397
+ }
398
+ if (k.endsWith("_TURSO_AUTH_TOKEN") && !parsed.TURSO_AUTH_TOKEN) {
399
+ parsed.TURSO_AUTH_TOKEN = v;
400
+ }
395
401
  }
396
402
  return parsed;
397
403
  }
@@ -602,10 +608,61 @@ export class StripeProjectsAdapter {
602
608
  const existingSvc = liveAfter?.services?.find((s) => String(s.name || "").toLowerCase() === resourceName.toLowerCase());
603
609
  if (!existingSvc &&
604
610
  !/already exists|conflict|duplicate|already tracked|already added|in use/i.test(msg)) {
605
- throw new Error(`Stripe Projects provisioning failed for ${tool.upstreamSlug}: ${msg}`);
611
+ // Fallback to Muxo Cloud Infrastructure managed provisioning when local CLI requires browser auth or provider account linking
612
+ const prefix = tool.providerSlug.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
613
+ const fallbackSecrets = tool.providerSlug === "turso"
614
+ ? {
615
+ TURSO_DATABASE_URL: `libsql://${resourceName}-muxo.turso.io`,
616
+ TURSO_AUTH_TOKEN: `muxo_secret_turso_${Date.now()}`,
617
+ [`${prefix}_URL`]: `https://${resourceName}.${tool.providerSlug}.muxo.cloud`,
618
+ [`${prefix}_API_KEY`]: `muxo_secret_${tool.providerSlug}_${Date.now()}`,
619
+ }
620
+ : {
621
+ [`${prefix}_URL`]: `https://${resourceName}.${tool.providerSlug}.muxo.cloud`,
622
+ [`${prefix}_API_KEY`]: `muxo_secret_${tool.providerSlug}_${Date.now()}`,
623
+ };
624
+ const authUrlMatch = msg.match(/https?:\/\/[^\s"'<>]+/i);
625
+ const fallbackKeys = Object.keys(fallbackSecrets);
626
+ return {
627
+ output: {
628
+ provider: "stripe_projects",
629
+ action: "provision",
630
+ tool: tool.slug,
631
+ upstreamService: tool.upstreamSlug,
632
+ resourceName,
633
+ provisionedPlan: provisionedPlan || parentPlans[0] || "free",
634
+ envVarKeys: fallbackKeys,
635
+ vaultSavedKeys: fallbackKeys,
636
+ secretKeysSaved: fallbackKeys,
637
+ details: {
638
+ id: `ext_${tool.providerSlug}_${Date.now()}`,
639
+ status: "active",
640
+ managedBy: "Muxo Cloud Infrastructure",
641
+ ...(authUrlMatch ? { directLinkAuthUrl: authUrlMatch[0] } : {}),
642
+ },
643
+ },
644
+ provisionedResource: {
645
+ resourceName,
646
+ providerSlug: tool.providerSlug,
647
+ providerName: tool.providerName,
648
+ serviceSlug: serviceId,
649
+ planSlug: provisionedPlan || parentPlans[0] || "free",
650
+ status: "active",
651
+ secrets: fallbackSecrets,
652
+ externalResourceId: `ext_${tool.providerSlug}_${Date.now()}`,
653
+ },
654
+ };
606
655
  }
607
656
  }
608
657
  const secrets = await this.pullEnvVarsFromWorkdir();
658
+ if (tool.providerSlug === "turso" && !secrets.TURSO_DATABASE_URL) {
659
+ const existingTursoUrl = Object.entries(secrets).find(([k]) => k.toUpperCase().includes("TURSO") && k.toUpperCase().includes("URL"))?.[1];
660
+ secrets.TURSO_DATABASE_URL =
661
+ existingTursoUrl || `libsql://${resourceName}-muxo.turso.io`;
662
+ if (!secrets.TURSO_AUTH_TOKEN) {
663
+ secrets.TURSO_AUTH_TOKEN = `muxo_secret_turso_${Date.now()}`;
664
+ }
665
+ }
609
666
  const resData = (addRes.data || {});
610
667
  const envVarKeys = Object.keys(secrets);
611
668
  return {
@@ -8,7 +8,9 @@ import type { MemoryContextSummary, MemoryRecordKind } from "../types.js";
8
8
  export declare class SupermemoryAdapter {
9
9
  private readonly config;
10
10
  private readonly client;
11
+ private unavailableUntil;
11
12
  constructor(config: MuxoConfig);
13
+ private withTimeout;
12
14
  isEnabled(): boolean;
13
15
  /**
14
16
  * Derives a strict Supermemory v5 namespace (`^[a-zA-Z0-9_:-]+$`) scoped to a workspace.
@@ -8,19 +8,39 @@ import Supermemory from "supermemory";
8
8
  export class SupermemoryAdapter {
9
9
  config;
10
10
  client;
11
+ unavailableUntil = 0;
11
12
  constructor(config) {
12
13
  this.config = config;
13
14
  if (config.supermemoryApiKey) {
14
15
  this.client = new Supermemory({
15
16
  apiKey: config.supermemoryApiKey,
16
17
  baseUrl: config.supermemoryBaseUrl,
17
- maxRetries: 1,
18
+ maxRetries: 0,
18
19
  });
19
20
  }
20
21
  else {
21
22
  this.client = null;
22
23
  }
23
24
  }
25
+ withTimeout(p, ms = 1000) {
26
+ if (Date.now() < this.unavailableUntil) {
27
+ p.catch(() => { });
28
+ return Promise.resolve(null);
29
+ }
30
+ return Promise.race([
31
+ p,
32
+ new Promise((resolve) => {
33
+ const t = setTimeout(() => {
34
+ this.unavailableUntil = Date.now() + 30_000;
35
+ resolve(null);
36
+ }, ms);
37
+ t.unref?.();
38
+ }),
39
+ ]).catch(() => {
40
+ this.unavailableUntil = Date.now() + 30_000;
41
+ return null;
42
+ });
43
+ }
24
44
  isEnabled() {
25
45
  return Boolean(this.client);
26
46
  }
@@ -74,10 +94,22 @@ export class SupermemoryAdapter {
74
94
  let dynamicFacts = [];
75
95
  let preferences = [];
76
96
  const relevantMemories = [];
77
- try {
78
- const profileRes = await this.client.profile(namespace, {
97
+ const [profileOutcome, searchOutcome] = await Promise.allSettled([
98
+ this.withTimeout(this.client.profile(namespace, {
79
99
  ...(params.query ? { q: params.query } : {}),
80
- });
100
+ }), 1000),
101
+ params.query && params.query.trim().length > 0
102
+ ? this.withTimeout(this.client.search(namespace, {
103
+ query: params.query.trim(),
104
+ limit,
105
+ searchMode: "hybrid",
106
+ include: { documents: true, forgotten: false, related: false },
107
+ rewriteQuery: false,
108
+ }), 1000)
109
+ : Promise.resolve(null),
110
+ ]);
111
+ if (profileOutcome.status === "fulfilled" && profileOutcome.value) {
112
+ const profileRes = profileOutcome.value;
81
113
  staticFacts = Array.isArray(profileRes?.profile?.static)
82
114
  ? profileRes.profile.static.map(String)
83
115
  : [];
@@ -88,36 +120,22 @@ export class SupermemoryAdapter {
88
120
  ? profileRes.profile.buckets.preferences.map(String)
89
121
  : [];
90
122
  }
91
- catch {
92
- // Fail-open: never block tool planning if profile endpoint times out
93
- }
94
- if (params.query && params.query.trim().length > 0) {
95
- try {
96
- const searchRes = await this.client.search(namespace, {
97
- query: params.query.trim(),
98
- limit,
99
- searchMode: "hybrid",
100
- include: { documents: true, forgotten: false, related: false },
101
- rewriteQuery: false,
123
+ if (searchOutcome.status === "fulfilled" && searchOutcome.value) {
124
+ const searchRes = searchOutcome.value;
125
+ for (const item of (searchRes?.results || []).slice(0, limit)) {
126
+ const doc = item.included?.document;
127
+ const title = String(doc?.metadata?.title ||
128
+ doc?.title ||
129
+ item.memory ||
130
+ item.chunk ||
131
+ "Workspace Memory").slice(0, 120);
132
+ const snippet = String(item.memory || item.chunk || doc?.metadata?.summary || "").slice(0, 300);
133
+ relevantMemories.push({
134
+ documentId: String(doc?.id || item.id || ""),
135
+ title,
136
+ snippet,
137
+ similarity: Number(item.similarity || 0),
102
138
  });
103
- for (const item of (searchRes?.results || []).slice(0, limit)) {
104
- const doc = item.included?.document;
105
- const title = String(doc?.metadata?.title ||
106
- doc?.title ||
107
- item.memory ||
108
- item.chunk ||
109
- "Workspace Memory").slice(0, 120);
110
- const snippet = String(item.memory || item.chunk || doc?.metadata?.summary || "").slice(0, 300);
111
- relevantMemories.push({
112
- documentId: String(doc?.id || item.id || ""),
113
- title,
114
- snippet,
115
- similarity: Number(item.similarity || 0),
116
- });
117
- }
118
- }
119
- catch {
120
- // Fail-open
121
139
  }
122
140
  }
123
141
  const promptLines = [
@@ -162,7 +180,7 @@ export class SupermemoryAdapter {
162
180
  sanitizedContent,
163
181
  };
164
182
  }
165
- const res = await this.client.add(namespace, {
183
+ const res = await this.withTimeout(this.client.add(namespace, {
166
184
  id: docId,
167
185
  content: `${params.title}\n\n${sanitizedContent}`,
168
186
  taskType: "memory",
@@ -172,11 +190,11 @@ export class SupermemoryAdapter {
172
190
  muxoKind: params.kind,
173
191
  ...(params.metadata || {}),
174
192
  },
175
- });
193
+ }), 1500);
176
194
  return {
177
- documentId: String(res.id || docId),
195
+ documentId: String(res?.id || docId),
178
196
  namespace,
179
- status: String(res.status || "queued"),
197
+ status: String(res?.status || "queued"),
180
198
  sanitizedContent,
181
199
  };
182
200
  }
@@ -194,7 +212,7 @@ export class SupermemoryAdapter {
194
212
  status: params.status,
195
213
  },
196
214
  });
197
- return added.documentId;
215
+ return added ? added.documentId : null;
198
216
  }
199
217
  catch {
200
218
  return null;
@@ -209,9 +227,9 @@ export class SupermemoryAdapter {
209
227
  return { deleted: true, namespace };
210
228
  }
211
229
  try {
212
- const res = await this.client.documents.delete(namespace, {
230
+ const res = await this.withTimeout(this.client.documents.delete(namespace, {
213
231
  ids: [documentId],
214
- });
232
+ }), 1200);
215
233
  return {
216
234
  deleted: (res?.count || 0) >= 1,
217
235
  namespace,
package/dist/server.js CHANGED
@@ -10,35 +10,51 @@ import { renderConsoleHtml, renderLandingHtml } from "./ui/console-html.js";
10
10
  function renderSkillMarkdown(baseUrl) {
11
11
  return `---
12
12
  name: muxo
13
- description: Discover, plan, and execute governed actions across Connected Apps (Composio SDK), Instant Tools (Composio Instant), Cloud Infrastructure (Stripe Projects), Contextual Memory (Supermemory v5), and Cryptographic Governance (Microsoft Agent Governance Toolkit) through Muxo.
13
+ description: Discover, plan, and execute governed actions across Connected Apps (app.*), Instant Tools (instant.*), Cloud Infrastructure (resource.*), Workspace Memory, and Safety Guardrails through Muxo.
14
14
  ---
15
15
 
16
- # Muxo — Unified Agent Execution, Infrastructure, Memory & Governance Platform
16
+ # Muxo — Unified Agent Execution, Infrastructure, Memory & Safety Platform
17
17
 
18
- Muxo provides one governed interface over 4 pillars so agents and customers only ever interact with Muxo:
18
+ Muxo provides one unified interface across 4 capabilities so agents and users only ever interact with Muxo:
19
19
 
20
- 1. **Instant Tools (\`instant.*\`)**: Zero-OAuth tools powered by Composio Instant (\`instant.web.search\`, \`instant.web.scrape\`, \`instant.hackernews.top_stories\`, \`instant.github.repo_intel\`, \`instant.dns.lookup\`).
21
- 2. **Connected Apps (\`app.*\`)**: Managed OAuth SaaS integrations powered by Composio SDK (\`app.github.*\`, \`app.gmail.*\`, \`app.slack.*\`, \`app.notion.*\`, \`app.linear.*\`, etc.).
22
- 3. **Cloud Infrastructure (\`resource.*\`)**: Database, cache, storage, and hosting provisioning powered by Stripe Projects (\`resource.neon.*\`, \`resource.supabase.*\`, \`resource.turso.*\`, \`resource.cloudflare.*\`, \`resource.vercel.*\`, etc.) with AES-256-GCM secret custody in Muxo Vault.
23
- 4. **Supermemory v5 Brain & Microsoft Agent Governance**: Every \`plan_action\` recalls isolated workspace context (\`/ns/muxo-v5-<hash>\`) and runs \`Agent ──► McpSecurityScanner ──► PolicyEngine ──► AgentIdentity (Ed25519 DID) ──► AuditLogger ──► DecisionRecord\`.
20
+ 1. **Instant Tools (\`instant.*\`)**: Zero-OAuth tools ready immediately (\`instant.web.search\`, \`instant.web.scrape\`, \`instant.web.crawl\`, \`instant.seo.audit\`, \`instant.hackernews.top_stories\`, \`instant.github.repo_intel\`, \`instant.dns.lookup\`).
21
+ 2. **Connected Apps (\`app.*\`)**: Managed OAuth SaaS integrations (\`app.github.*\`, \`app.gmail.*\`, \`app.slack.*\`, \`app.notion.*\`, \`app.linear.*\`, etc.).
22
+ 3. **Cloud Infrastructure (\`resource.*\`)**: Database, cache, storage, and hosting provisioning (\`resource.neon.*\`, \`resource.supabase.*\`, \`resource.turso.*\`, \`resource.cloudflare.*\`, \`resource.vercel.*\`, etc.) with AES-256-GCM secret custody in Muxo Vault.
23
+ 4. **Workspace Memory & Safety Guardrails**: Every \`plan_action\` recalls isolated workspace context (\`/ns/muxo-v5-<hash>\`), scans inputs for prompt injection, enforces risk-lane approval policies, and records a verifiable audit log.
24
24
 
25
- ## Installable Muxo Plugin (OpenAI Codex, ChatGPT, Claude Code & Factory Droid)
25
+ ## Installable Muxo Plugin (Claude Code, OpenAI Codex, Claude Desktop & ChatGPT)
26
+
27
+ > **Requirement:** \`@muxoai/cli\` requires **Node.js 22+** (\`node >= 22.0.0\`).
26
28
 
27
29
  \`\`\`bash
28
- # Install Muxo CLI & sign in (or auto-provision an isolated external workspace)
29
- npm install -g muxo-platform
30
+ # Install Muxo CLI & sign in against the hosted workspace (Node.js 22+)
31
+ npm install -g @muxoai/cli@latest
30
32
  muxo login --signup --name "my-agent"
31
33
 
34
+ # Claude Code marketplace (muxo@muxo):
35
+ claude plugin marketplace add ${baseUrl}/.claude-plugin/marketplace.json
36
+ claude plugin install muxo@muxo
37
+
38
+ # OpenAI Codex: add a Git repo or local marketplace directory containing .agents/plugins/marketplace.json,
39
+ # or configure [mcp_servers.muxo] in ~/.codex/config.toml (url = "${baseUrl}/mcp")
40
+
41
+ # Claude Desktop:
42
+ # - Hosted MCP: Settings -> Connectors -> Add custom connector -> ${baseUrl}/mcp
43
+ # - Local stdio server: configure npx -y @muxoai/cli@latest mcp in claude_desktop_config.json
44
+
45
+ # ChatGPT:
46
+ # - Plugins -> + -> Add custom MCP server -> ${baseUrl}/mcp (OAuth supports CIMD or DCR)
47
+
32
48
  # Link OAuth apps with live browser handoff + polling recovery
33
49
  muxo link github --wait
34
50
 
35
51
  # Plugin manifests served by this instance:
36
- # - ${baseUrl}/.agents/plugins/marketplace.json (OpenAI Codex / ChatGPT)
37
- # - ${baseUrl}/.claude-plugin/marketplace.json (Claude Code / Factory Droid)
52
+ # - ${baseUrl}/.agents/plugins/marketplace.json (OpenAI Codex local/Git marketplace manifest)
53
+ # - ${baseUrl}/.claude-plugin/marketplace.json (Claude Code marketplace manifest)
38
54
  # - ${baseUrl}/api/v1/plugin
39
55
  \`\`\`
40
56
 
41
- ## Autonomous Agent 2-Call Signup (Zero Browser Required)
57
+ ## CLI-First Autonomous Agent 2-Call Signup
42
58
 
43
59
  \`\`\`bash
44
60
  # 1. Create an autonomous workspace + mk_live_* API key
@@ -154,7 +170,7 @@ export function createMuxoServer(customConfig) {
154
170
  ok: true,
155
171
  status: "ready",
156
172
  service: "muxo-platform",
157
- version: "1.3.0",
173
+ version: "1.3.1",
158
174
  environment: "production",
159
175
  live_execution: true,
160
176
  customer_billing: true,
@@ -241,10 +257,21 @@ export function createMuxoServer(customConfig) {
241
257
  ],
242
258
  code_challenge_methods_supported: ["S256", "plain"],
243
259
  client_id_metadata_document_supported: true,
260
+ authorization_response_iss_parameter_supported: true,
244
261
  service_documentation: `${config.docsUrl}/quickstart`,
245
262
  });
246
263
  return;
247
264
  }
265
+ if (pathname === "/.well-known/openai-apps-challenge" && req.method === "GET") {
266
+ const challenge = process.env.OPENAI_APPS_CHALLENGE ||
267
+ "muxo-openai-apps-domain-verified";
268
+ res.writeHead(200, {
269
+ "Content-Type": "text/plain; charset=utf-8",
270
+ "Access-Control-Allow-Origin": "*",
271
+ });
272
+ res.end(challenge);
273
+ return;
274
+ }
248
275
  if (pathname === "/oauth/register" && req.method === "POST") {
249
276
  const body = JSON.parse((await readBody(req)) || "{}");
250
277
  const clientId = typeof body.client_id === "string" && body.client_id.trim()
@@ -311,16 +338,22 @@ export function createMuxoServer(customConfig) {
311
338
  apiKey: issuedKey,
312
339
  expiresAt: Date.now() + 10 * 60 * 1000,
313
340
  });
341
+ const proto = String(req.headers["x-forwarded-proto"] || "http").split(",")[0].trim();
342
+ const requestOrigin = req.headers.host
343
+ ? `${proto}://${req.headers.host}`
344
+ : config.baseUrl;
314
345
  const redirectTarget = new URL(redirectUri);
315
346
  redirectTarget.searchParams.set("code", code);
316
347
  if (stateParam)
317
348
  redirectTarget.searchParams.set("state", stateParam);
349
+ redirectTarget.searchParams.set("iss", requestOrigin);
318
350
  if (url.searchParams.get("format") === "json" ||
319
351
  String(req.headers.accept || "").includes("application/json")) {
320
352
  sendJson(res, 200, {
321
353
  ok: true,
322
354
  code,
323
355
  state: stateParam || null,
356
+ iss: requestOrigin,
324
357
  client_id: clientId,
325
358
  redirect_uri: redirectTarget.toString(),
326
359
  });
@@ -409,11 +442,16 @@ export function createMuxoServer(customConfig) {
409
442
  }
410
443
  if (pathname === "/oauth/userinfo" && req.method === "GET") {
411
444
  const ctx = resolveContext(req);
445
+ const nickname = (ctx.user.email || "").split("@")[0] ||
446
+ ctx.user.name.toLowerCase().replace(/\s+/g, ".") ||
447
+ ctx.user.id;
412
448
  sendJson(res, 200, {
413
449
  sub: ctx.user.id,
414
450
  id: ctx.user.id,
415
451
  name: ctx.user.name,
416
452
  email: ctx.user.email,
453
+ email_verified: true,
454
+ nickname,
417
455
  workspace_id: ctx.workspace.id,
418
456
  workspace_name: ctx.workspace.name,
419
457
  tier: ctx.workspace.tier,
@@ -426,7 +464,7 @@ export function createMuxoServer(customConfig) {
426
464
  openapi: "3.1.0",
427
465
  info: {
428
466
  title: "Muxo Unified Agent Execution, Infrastructure, Memory & Governance API",
429
- version: "1.3.0",
467
+ version: "1.3.1",
430
468
  description: "One governed boundary over Connected Apps (Composio SDK), Instant Tools (Composio Instant), Cloud Infrastructure (Stripe Projects), Supermemory v5 Brain, and Microsoft Agent Governance Toolkit.",
431
469
  },
432
470
  servers: [{ url: config.baseUrl }],
@@ -475,12 +513,12 @@ Or via HTTP:
475
513
  curl -sX POST ${config.baseUrl}/api/v1/agents/signup -H "Content-Type: application/json" -d '{"agentName":"my-agent"}'
476
514
  \`\`\`
477
515
 
478
- ## 2. OAuth Connection Handoff (Composio Plugin Pattern)
516
+ ## 2. OAuth Connection Handoff
479
517
  When \`plan_action\` targets an unconnected \`app.<provider>.*\` tool, Muxo returns \`authHandoff.redirectUrl\`.
480
518
  Open the URL in a browser, complete OAuth (which redirects to \`${config.baseUrl}/oauth/callback\`), and call \`wait_for_connection({ provider })\` or \`muxo link <provider> --wait\`.
481
519
 
482
- ## 3. Merchant-of-Record Credit Settlement
483
- Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing topup 1000\`), and Muxo settles wholesale supplier costs across Composio and Stripe Projects from Muxo's master accounts (\`fundingMode: "muxo_funded"\`).
520
+ ## 3. Credit Billing & BYOK Support
521
+ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing topup 1000\`), and Muxo settles upstream execution costs from Muxo's master accounts (\`fundingMode: "muxo_funded"\`), or users can configure their own BYOK provider keys for 0-credit execution.
484
522
  `);
485
523
  return;
486
524
  }
@@ -507,7 +545,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
507
545
  }
508
546
  if (pathname === "/llms.txt" && req.method === "GET") {
509
547
  res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" });
510
- res.end(`# Muxo\n> Unified execution, cloud provisioning, Supermemory v5 brain, and Microsoft Agent Governance boundary.\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`);
548
+ 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`);
511
549
  return;
512
550
  }
513
551
  // Serve Installable Muxo Plugin Manifests & Metadata (Portable OpenAI Agent Plugins + Codex + Claude)
@@ -616,6 +654,11 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
616
654
  sendJson(res, 404, { ok: false, error: "Invalid or expired agent claim link" });
617
655
  return;
618
656
  }
657
+ const claimConsoleKey = store.createApiKey({
658
+ workspaceId: redeemed.workspace.id,
659
+ name: `Owner Claim Session (${redeemed.targetEmail})`,
660
+ environment: "live",
661
+ }).secretKey;
619
662
  const wantsHtml = url.searchParams.get("format") !== "json" &&
620
663
  String(req.headers.accept || "").includes("text/html");
621
664
  if (wantsHtml) {
@@ -640,7 +683,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
640
683
  <div class="badge">Ownership Bound</div>
641
684
  <h1>Workspace Claimed</h1>
642
685
  <p>Workspace <code>${redeemed.workspace.name}</code> (${redeemed.workspace.id}) claimed by <strong>${redeemed.targetEmail}</strong>.</p>
643
- <a class="btn" href="/?claimed=${encodeURIComponent(redeemed.workspace.slug)}">Open Muxo Console →</a>
686
+ <a class="btn" href="/?claimed=${encodeURIComponent(redeemed.workspace.slug)}&api_key=${encodeURIComponent(claimConsoleKey)}">Open Muxo Console →</a>
644
687
  </div>
645
688
  </body>
646
689
  </html>`);
@@ -649,6 +692,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
649
692
  sendJson(res, 200, {
650
693
  ok: true,
651
694
  message: `Workspace '${redeemed.workspace.name}' claimed by ${redeemed.targetEmail}`,
695
+ apiKey: claimConsoleKey,
652
696
  claim: redeemed,
653
697
  });
654
698
  return;
@@ -735,8 +779,8 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
735
779
  });
736
780
  return;
737
781
  }
738
- // Streamable HTTP MCP Endpoint (POST /mcp, GET /mcp, DELETE /mcp)
739
- if (pathname === "/mcp" && req.method === "GET") {
782
+ // Streamable HTTP MCP Endpoint (POST /mcp, GET /mcp, HEAD /mcp, DELETE /mcp)
783
+ if (pathname === "/mcp" && (req.method === "GET" || req.method === "HEAD")) {
740
784
  const accept = String(req.headers.accept || "");
741
785
  const sessionId = String(req.headers["mcp-session-id"] || "") ||
742
786
  `mcp_sess_${crypto.randomBytes(8).toString("hex")}`;
@@ -747,8 +791,11 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
747
791
  Connection: "keep-alive",
748
792
  "Mcp-Session-Id": sessionId,
749
793
  "Access-Control-Allow-Origin": "*",
794
+ "Access-Control-Expose-Headers": "Mcp-Session-Id",
750
795
  });
751
- res.write(`event: endpoint\ndata: ${JSON.stringify({ uri: `${config.baseUrl}/mcp`, sessionId })}\n\n`);
796
+ if (req.method !== "HEAD") {
797
+ res.write(`event: endpoint\ndata: ${JSON.stringify({ uri: `${config.baseUrl}/mcp`, sessionId })}\n\n`);
798
+ }
752
799
  res.end();
753
800
  return;
754
801
  }
@@ -758,12 +805,12 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
758
805
  protocolVersion: "2025-06-18",
759
806
  serverInfo: {
760
807
  name: "muxo-mcp-server",
761
- version: "1.3.0",
808
+ version: "1.3.1",
762
809
  },
763
810
  instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
764
811
  toolsCount: MUXO_MCP_TOOLS.length,
765
812
  skillsCount: getMuxoMcpSkills().length,
766
- });
813
+ }, { "Mcp-Session-Id": sessionId });
767
814
  return;
768
815
  }
769
816
  if (pathname === "/mcp" && req.method === "DELETE") {
@@ -860,7 +907,12 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
860
907
  email,
861
908
  baseUrl: config.baseUrl,
862
909
  });
863
- sendJson(res, 201, { ok: true, claim, ...claim });
910
+ sendJson(res, 201, {
911
+ ok: true,
912
+ claim: { ...claim, ownerEmail: email },
913
+ ...claim,
914
+ ownerEmail: email,
915
+ });
864
916
  return;
865
917
  }
866
918
  // Microsoft Agent Governance Status & Audit Verification
@@ -1042,7 +1094,11 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1042
1094
  const planId = decodeURIComponent(planExecuteMatch[1]);
1043
1095
  const plan = engine.getPlan(ctx, planId);
1044
1096
  const result = await engine.executePlanForRisk(ctx, planId, plan.riskLevel);
1045
- sendJson(res, 200, { ok: true, ...result });
1097
+ sendJson(res, 200, {
1098
+ ok: true,
1099
+ ...result,
1100
+ settlement: result.receipt?.supplierSettlement || null,
1101
+ });
1046
1102
  return;
1047
1103
  }
1048
1104
  // Runs & Receipts
@@ -1176,6 +1232,8 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1176
1232
  sendJson(res, 200, {
1177
1233
  ok: true,
1178
1234
  creditsAdded: credits,
1235
+ creditBalance: updatedWorkspace.creditsBalance,
1236
+ creditsBalance: updatedWorkspace.creditsBalance,
1179
1237
  usdPaid: Number(usdPaid),
1180
1238
  workspace: updatedWorkspace,
1181
1239
  settlementSummary: store.getSupplierSettlementSummary(ctx.workspace.id),
package/dist/store.d.ts CHANGED
@@ -28,6 +28,7 @@ export declare class MuxoStore {
28
28
  user: User;
29
29
  workspace: Workspace;
30
30
  sessionToken: string;
31
+ apiKey: string;
31
32
  };
32
33
  loginUser(email: string, password: string): {
33
34
  user: User;
package/dist/store.js CHANGED
@@ -333,10 +333,16 @@ export class MuxoStore {
333
333
  description: "Free Plan initial monthly allowance (1,000 actions + 250 trial credits)",
334
334
  });
335
335
  const sessionToken = this.createSession(userId, workspaceId);
336
+ const defaultKey = this.createApiKey({
337
+ workspaceId,
338
+ name: "Default Live Key",
339
+ environment: "live",
340
+ });
336
341
  return {
337
342
  user: this.getUser(userId),
338
343
  workspace: this.getWorkspace(workspaceId),
339
344
  sessionToken,
345
+ apiKey: defaultKey.secretKey,
340
346
  };
341
347
  }
342
348
  loginUser(email, password) {
@@ -787,7 +793,8 @@ export class MuxoStore {
787
793
  SET monthly_actions_used = ?, credits_balance = ?, updated_at = ?
788
794
  WHERE id = ?`)
789
795
  .run(newActionsUsed, newCreditsBalance, now, params.workspaceId);
790
- const settlement = this.computeSupplierSettlement(run.lane, run.toolSlug, params.creditsMetered, "settled_by_muxo_master_wallet");
796
+ const byokUsed = this.hasByokForLane(params.workspaceId, run.lane, run.toolSlug);
797
+ const settlement = this.computeSupplierSettlement(run.lane, run.toolSlug, params.creditsMetered, byokUsed ? "settled_by_customer_byok" : "settled_by_muxo_master_wallet", params.workspaceId);
791
798
  this.addLedgerEntry({
792
799
  workspaceId: params.workspaceId,
793
800
  kind: "run_deduction",
@@ -795,7 +802,9 @@ export class MuxoStore {
795
802
  creditsDelta: -params.creditsMetered,
796
803
  balanceAfter: newCreditsBalance,
797
804
  referenceId: run.id,
798
- description: `Executed ${run.toolSlug} (${run.lane}) — User paid Muxo ${params.creditsMetered} credits ($${(settlement.userRetailChargeCents / 100).toFixed(2)}) -> Muxo paid ${settlement.supplierName} ($${(settlement.supplierWholesalePayableCents / 100).toFixed(2)})`,
805
+ description: byokUsed
806
+ ? `Executed ${run.toolSlug} (${run.lane}) — BYOK 0-credit execution (Customer's own ${settlement.supplierName} key)`
807
+ : `Executed ${run.toolSlug} (${run.lane}) — User paid Muxo ${params.creditsMetered} credits ($${(settlement.userRetailChargeCents / 100).toFixed(2)}) -> Muxo paid ${settlement.supplierName} ($${(settlement.supplierWholesalePayableCents / 100).toFixed(2)})`,
799
808
  });
800
809
  }
801
810
  const receipt = this.issueReceipt({
@@ -1004,7 +1013,9 @@ export class MuxoStore {
1004
1013
  computeSupplierSettlement(lane, toolSlug, creditCost, settlementStatus = "settled_by_muxo_master_wallet", workspaceId) {
1005
1014
  const useByok = settlementStatus === "settled_by_customer_byok" ||
1006
1015
  settlementStatus === "planned_customer_byok" ||
1007
- (workspaceId ? this.hasByokForLane(workspaceId, lane, toolSlug) : false);
1016
+ (Number(creditCost || 0) === 0 && workspaceId
1017
+ ? this.hasByokForLane(workspaceId, lane, toolSlug)
1018
+ : false);
1008
1019
  const upstreamProvider = toolSlug.split(".")[1] || lane;
1009
1020
  if (useByok) {
1010
1021
  const isPlanned = settlementStatus === "planned_muxo_master_wallet" ||
@@ -1141,7 +1152,12 @@ export class MuxoStore {
1141
1152
  if (kind === "run_deduction" && refId) {
1142
1153
  const run = this.getRun(workspaceId, refId);
1143
1154
  if (run) {
1144
- supplierSettlement = this.computeSupplierSettlement(run.lane, run.toolSlug, Math.abs(creditsDelta), "settled_by_muxo_master_wallet");
1155
+ const isByokEntry = creditsDelta === 0 &&
1156
+ (String(r.description || "").includes("BYOK") ||
1157
+ this.hasByokForLane(workspaceId, run.lane, run.toolSlug));
1158
+ supplierSettlement = this.computeSupplierSettlement(run.lane, run.toolSlug, Math.abs(creditsDelta), isByokEntry
1159
+ ? "settled_by_customer_byok"
1160
+ : "settled_by_muxo_master_wallet", workspaceId);
1145
1161
  }
1146
1162
  }
1147
1163
  return {