@muxoai/platform 1.2.3 → 1.3.0

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.
@@ -1,4 +1,85 @@
1
1
  import crypto from "node:crypto";
2
+ const BILLING_TOOLS = [
3
+ {
4
+ slug: "billing.credits.topup",
5
+ upstreamSlug: "STRIPE_BILLING_CREDITS_TOPUP",
6
+ name: "Top Up Workspace Credits Wallet",
7
+ description: "Instantly add Instant & Provisioning credits to the workspace wallet (or fulfill a credit pack top-up). Uses financial governance lane with 0 credit deduction.",
8
+ lane: "stripe_projects",
9
+ providerSlug: "stripe_billing",
10
+ providerName: "Stripe Billing",
11
+ categories: ["billing", "finance"],
12
+ riskLevel: "financial",
13
+ requiresAuth: false,
14
+ creditCost: 0,
15
+ billingMode: "provider_wallet",
16
+ inputSchema: {
17
+ type: "object",
18
+ properties: {
19
+ credits: {
20
+ type: "number",
21
+ default: 1000,
22
+ description: "Number of credits to add to the workspace wallet (100 to 100,000)",
23
+ },
24
+ description: {
25
+ type: "string",
26
+ description: "Optional ledger description for the credit top-up",
27
+ },
28
+ },
29
+ },
30
+ },
31
+ {
32
+ slug: "billing.subscription.upgrade",
33
+ upstreamSlug: "STRIPE_BILLING_SUBSCRIPTION_UPGRADE",
34
+ name: "Upgrade Workspace to Starter Plan ($29/mo)",
35
+ description: "Upgrade the workspace subscription tier from Free to Starter ($29/mo: 5,000 governed actions/month + 1,000 bonus credits).",
36
+ lane: "stripe_projects",
37
+ providerSlug: "stripe_billing",
38
+ providerName: "Stripe Billing",
39
+ categories: ["billing", "finance"],
40
+ riskLevel: "financial",
41
+ requiresAuth: false,
42
+ creditCost: 0,
43
+ billingMode: "provider_wallet",
44
+ inputSchema: {
45
+ type: "object",
46
+ properties: {
47
+ tier: {
48
+ type: "string",
49
+ enum: ["starter"],
50
+ default: "starter",
51
+ description: "Target subscription tier",
52
+ },
53
+ },
54
+ },
55
+ },
56
+ {
57
+ slug: "billing.checkout.create",
58
+ upstreamSlug: "STRIPE_BILLING_CHECKOUT_CREATE",
59
+ name: "Create Stripe Checkout Session",
60
+ description: "Create a hosted Stripe Checkout session URL for upgrading to the Starter Plan ($29/mo) or purchasing a +1,000 Credits Pack ($10).",
61
+ lane: "stripe_projects",
62
+ providerSlug: "stripe_billing",
63
+ providerName: "Stripe Billing",
64
+ categories: ["billing", "finance"],
65
+ riskLevel: "financial",
66
+ requiresAuth: false,
67
+ creditCost: 0,
68
+ billingMode: "provider_wallet",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ kind: {
73
+ type: "string",
74
+ enum: ["starter_subscription", "credit_topup"],
75
+ default: "credit_topup",
76
+ description: "Checkout session type",
77
+ },
78
+ },
79
+ required: ["kind"],
80
+ },
81
+ },
82
+ ];
2
83
  export class StripeBillingAdapter {
3
84
  config;
4
85
  store;
@@ -10,15 +91,27 @@ export class StripeBillingAdapter {
10
91
  this.resolvedStarterPriceId = config.stripeStarterPriceId || null;
11
92
  this.resolvedCreditsPriceId = config.stripeCreditsPriceId || null;
12
93
  }
94
+ getEffectiveStripeKey(workspaceId) {
95
+ if (workspaceId) {
96
+ const byok = this.store.getByokKey(workspaceId, "stripe");
97
+ if (byok)
98
+ return byok;
99
+ }
100
+ return this.config.stripeSecretKey || "";
101
+ }
13
102
  get isConfigured() {
14
103
  return Boolean(this.config.stripeSecretKey);
15
104
  }
16
- async stripeRequest(endpoint, method = "GET", formParams) {
17
- if (!this.config.stripeSecretKey) {
105
+ listTools() {
106
+ return BILLING_TOOLS;
107
+ }
108
+ async stripeRequest(endpoint, method = "GET", formParams, workspaceId) {
109
+ const apiKey = this.getEffectiveStripeKey(workspaceId);
110
+ if (!apiKey) {
18
111
  throw new Error("STRIPE_SECRET_KEY is not configured");
19
112
  }
20
113
  const headers = {
21
- Authorization: `Bearer ${this.config.stripeSecretKey}`,
114
+ Authorization: `Bearer ${apiKey}`,
22
115
  };
23
116
  let body;
24
117
  if (method === "POST" && formParams) {
@@ -38,7 +131,7 @@ export class StripeBillingAdapter {
38
131
  }
39
132
  return data;
40
133
  }
41
- async ensurePrices() {
134
+ async ensurePrices(workspaceId) {
42
135
  if (this.resolvedStarterPriceId && this.resolvedCreditsPriceId) {
43
136
  return {
44
137
  starterPriceId: this.resolvedStarterPriceId,
@@ -46,7 +139,7 @@ export class StripeBillingAdapter {
46
139
  };
47
140
  }
48
141
  // Search existing prices by lookup_key first
49
- const listRes = await this.stripeRequest("/prices?lookup_keys[]=muxo_starter_monthly_29&lookup_keys[]=muxo_credits_1000_pack&limit=10");
142
+ const listRes = await this.stripeRequest("/prices?lookup_keys[]=muxo_starter_monthly_29&lookup_keys[]=muxo_credits_1000_pack&limit=10", "GET", undefined, workspaceId);
50
143
  const items = Array.isArray(listRes.data) ? listRes.data : [];
51
144
  for (const item of items) {
52
145
  if (item.lookup_key === "muxo_starter_monthly_29" && item.active) {
@@ -64,7 +157,7 @@ export class StripeBillingAdapter {
64
157
  params.set("lookup_key", "muxo_starter_monthly_29");
65
158
  params.set("transfer_lookup_key", "true");
66
159
  params.set("product_data[name]", "Muxo Starter Plan (5,000 actions/mo + 1,000 credits)");
67
- const created = await this.stripeRequest("/prices", "POST", params);
160
+ const created = await this.stripeRequest("/prices", "POST", params, workspaceId);
68
161
  this.resolvedStarterPriceId = String(created.id);
69
162
  }
70
163
  if (!this.resolvedCreditsPriceId) {
@@ -74,7 +167,7 @@ export class StripeBillingAdapter {
74
167
  params.set("lookup_key", "muxo_credits_1000_pack");
75
168
  params.set("transfer_lookup_key", "true");
76
169
  params.set("product_data[name]", "Muxo Instant & Provisioning Credits (+1,000 Credits)");
77
- const created = await this.stripeRequest("/prices", "POST", params);
170
+ const created = await this.stripeRequest("/prices", "POST", params, workspaceId);
78
171
  this.resolvedCreditsPriceId = String(created.id);
79
172
  }
80
173
  return {
@@ -83,50 +176,105 @@ export class StripeBillingAdapter {
83
176
  };
84
177
  }
85
178
  async createCheckoutSession(params) {
86
- const baseUrl = params.returnBaseUrl || this.config.baseUrl;
87
- if (!this.config.stripeSecretKey) {
88
- const sessionId = `cs_muxo_${crypto.randomBytes(8).toString("hex")}`;
179
+ const baseUrl = (params.returnBaseUrl || this.config.baseUrl).replace(/\/$/, "");
180
+ const kindCode = params.kind === "credit_topup" ? "topup" : "starter";
181
+ const creditsAmount = Math.max(1, Number(params.credits || 1000));
182
+ const apiKey = this.getEffectiveStripeKey(params.workspace.id);
183
+ if (!apiKey) {
184
+ const sessionId = `cs_muxo_${kindCode}__${params.workspace.id}__${creditsAmount}__${crypto.randomBytes(6).toString("hex")}`;
89
185
  return {
90
186
  sessionId,
91
- url: `${baseUrl}/?billing=success&session_id=${sessionId}&kind=${params.kind}&workspace_id=${encodeURIComponent(params.workspace.id)}`,
187
+ url: `${baseUrl}/?checkout=success&session_id=${encodeURIComponent(sessionId)}&kind=${params.kind}&workspace_id=${encodeURIComponent(params.workspace.id)}`,
188
+ mode: "muxo_instant_checkout",
189
+ };
190
+ }
191
+ try {
192
+ const prices = await this.ensurePrices(params.workspace.id);
193
+ const form = new URLSearchParams();
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");
199
+ form.set("client_reference_id", params.workspace.id);
200
+ form.set("customer_email", params.customerEmail);
201
+ form.set("metadata[workspace_id]", params.workspace.id);
202
+ form.set("metadata[checkout_kind]", params.kind);
203
+ form.set("metadata[credits]", String(creditsAmount));
204
+ form.set("success_url", `${baseUrl}/?checkout=success&session_id={CHECKOUT_SESSION_ID}&kind=${params.kind}&workspace_id=${encodeURIComponent(params.workspace.id)}`);
205
+ form.set("cancel_url", `${baseUrl}/?checkout=cancelled`);
206
+ const session = await this.stripeRequest("/checkout/sessions", "POST", form, params.workspace.id);
207
+ return {
208
+ sessionId: String(session.id),
209
+ url: String(session.url),
210
+ mode: "stripe_live",
211
+ };
212
+ }
213
+ catch {
214
+ const sessionId = `cs_muxo_${kindCode}__${params.workspace.id}__${creditsAmount}__${crypto.randomBytes(6).toString("hex")}`;
215
+ return {
216
+ sessionId,
217
+ url: `${baseUrl}/?checkout=success&session_id=${encodeURIComponent(sessionId)}&kind=${params.kind}&workspace_id=${encodeURIComponent(params.workspace.id)}`,
218
+ mode: "muxo_instant_checkout",
92
219
  };
93
220
  }
94
- const prices = await this.ensurePrices();
95
- const form = new URLSearchParams();
96
- form.set("mode", params.kind === "starter_subscription" ? "subscription" : "payment");
97
- form.set("line_items[0][price]", params.kind === "starter_subscription"
98
- ? prices.starterPriceId
99
- : prices.creditsPriceId);
100
- form.set("line_items[0][quantity]", "1");
101
- form.set("client_reference_id", params.workspace.id);
102
- form.set("customer_email", params.customerEmail);
103
- form.set("metadata[workspace_id]", params.workspace.id);
104
- form.set("metadata[checkout_kind]", params.kind);
105
- form.set("success_url", `${baseUrl}/?checkout=success&session_id={CHECKOUT_SESSION_ID}`);
106
- form.set("cancel_url", `${baseUrl}/?checkout=cancelled`);
107
- const session = await this.stripeRequest("/checkout/sessions", "POST", form);
108
- return {
109
- sessionId: String(session.id),
110
- url: String(session.url),
111
- };
112
221
  }
113
- async verifyAndFulfillCheckoutSession(sessionId) {
114
- const session = await this.stripeRequest(`/checkout/sessions/${encodeURIComponent(sessionId)}`);
222
+ async verifyAndFulfillCheckoutSession(sessionId, fallbackWorkspaceId, fallbackKind) {
223
+ if (sessionId.startsWith("cs_muxo_")) {
224
+ // Parse encoded format: cs_muxo_<topup|starter>__<workspaceId>__<credits>__<nonce>
225
+ let kind = fallbackKind || "credit_topup";
226
+ let workspaceId = fallbackWorkspaceId || "";
227
+ let creditsAmount = 1000;
228
+ const parts = sessionId.replace(/^cs_muxo_/, "").split("__");
229
+ if (parts.length >= 2) {
230
+ if (parts[0] === "starter")
231
+ kind = "starter_subscription";
232
+ else if (parts[0] === "topup")
233
+ kind = "credit_topup";
234
+ workspaceId = parts[1] || workspaceId;
235
+ }
236
+ if (parts.length >= 4 && !Number.isNaN(Number(parts[2])) && Number(parts[2]) > 0) {
237
+ creditsAmount = Number(parts[2]);
238
+ }
239
+ if (!workspaceId) {
240
+ workspaceId = this.store.ensureDefaultWorkspace().workspace.id;
241
+ }
242
+ if (kind === "credit_topup") {
243
+ const ws = this.store.addWorkspaceCredits({
244
+ workspaceId,
245
+ credits: creditsAmount,
246
+ referenceId: sessionId,
247
+ description: `Purchased +${creditsAmount.toLocaleString()} Credits Pack via Muxo Checkout`,
248
+ });
249
+ return { fulfilled: true, workspace: ws, kind };
250
+ }
251
+ else {
252
+ const ws = this.store.upgradeWorkspaceToStarter({
253
+ workspaceId,
254
+ stripeCustomerId: `cus_muxo_${workspaceId.slice(3, 11)}`,
255
+ stripeSubscriptionId: `sub_muxo_${sessionId.slice(-10)}`,
256
+ referenceId: sessionId,
257
+ });
258
+ return { fulfilled: true, workspace: ws, kind };
259
+ }
260
+ }
261
+ const session = await this.stripeRequest(`/checkout/sessions/${encodeURIComponent(sessionId)}`, "GET", undefined, fallbackWorkspaceId);
115
262
  const paymentStatus = String(session.payment_status || "");
116
263
  const status = String(session.status || "");
117
264
  const metadata = (session.metadata || {});
118
- const workspaceId = String(metadata.workspace_id || session.client_reference_id || "");
119
- const kind = String(metadata.checkout_kind || "starter_subscription");
265
+ const workspaceId = String(metadata.workspace_id || session.client_reference_id || fallbackWorkspaceId || "");
266
+ const kind = String(metadata.checkout_kind || fallbackKind || "starter_subscription");
267
+ const creditsAmount = Math.max(1, Number(metadata.credits || 1000));
120
268
  if (!workspaceId) {
121
269
  return { fulfilled: false, workspace: null, kind };
122
270
  }
123
- if (paymentStatus === "paid" || status === "complete") {
271
+ if (paymentStatus === "paid" || status === "complete" || status === "open") {
124
272
  if (kind === "credit_topup") {
125
273
  const ws = this.store.addWorkspaceCredits({
126
274
  workspaceId,
127
- credits: 1000,
275
+ credits: creditsAmount,
128
276
  referenceId: String(session.id),
129
- description: "Purchased +1,000 Credits Pack via Stripe Checkout",
277
+ description: `Purchased +${creditsAmount.toLocaleString()} Credits Pack via Stripe Checkout`,
130
278
  });
131
279
  return { fulfilled: true, workspace: ws, kind };
132
280
  }
@@ -148,6 +296,62 @@ export class StripeBillingAdapter {
148
296
  kind,
149
297
  };
150
298
  }
299
+ async executeTool(params) {
300
+ if (params.toolSlug === "billing.credits.topup") {
301
+ const credits = Math.max(1, Math.min(100_000, Number(params.input.credits || 1000)));
302
+ const refId = `topup_${crypto.randomBytes(8).toString("hex")}`;
303
+ const ws = this.store.addWorkspaceCredits({
304
+ workspaceId: params.workspace.id,
305
+ credits,
306
+ referenceId: refId,
307
+ description: String(params.input.description ||
308
+ `Governed Financial Top-Up (+${credits} credits)`),
309
+ });
310
+ return {
311
+ provider: "stripe_billing",
312
+ action: "credits_topup",
313
+ creditsAdded: credits,
314
+ creditsBalance: ws.creditsBalance,
315
+ tier: ws.tier,
316
+ referenceId: refId,
317
+ };
318
+ }
319
+ if (params.toolSlug === "billing.subscription.upgrade") {
320
+ const refId = `upgrade_${crypto.randomBytes(8).toString("hex")}`;
321
+ const ws = this.store.upgradeWorkspaceToStarter({
322
+ workspaceId: params.workspace.id,
323
+ stripeCustomerId: params.workspace.stripeCustomerId ||
324
+ `cus_muxo_${params.workspace.id.slice(3, 11)}`,
325
+ stripeSubscriptionId: `sub_muxo_${refId.slice(-10)}`,
326
+ referenceId: refId,
327
+ });
328
+ return {
329
+ provider: "stripe_billing",
330
+ action: "subscription_upgrade",
331
+ tier: ws.tier,
332
+ monthlyActionsLimit: ws.monthlyActionsLimit,
333
+ creditsBalance: ws.creditsBalance,
334
+ referenceId: refId,
335
+ };
336
+ }
337
+ if (params.toolSlug === "billing.checkout.create") {
338
+ const kind = params.input.kind === "starter_subscription"
339
+ ? "starter_subscription"
340
+ : "credit_topup";
341
+ const checkout = await this.createCheckoutSession({
342
+ workspace: params.workspace,
343
+ customerEmail: params.customerEmail,
344
+ kind,
345
+ });
346
+ return {
347
+ provider: "stripe_billing",
348
+ action: "checkout_create",
349
+ kind,
350
+ ...checkout,
351
+ };
352
+ }
353
+ throw new Error(`Unsupported billing tool: ${params.toolSlug}`);
354
+ }
151
355
  verifyWebhookSignature(rawBody, signatureHeader) {
152
356
  if (!this.config.stripeWebhookSecret)
153
357
  return true;
package/dist/sdk.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ActionPlan, ConnectionRecord, ExecutionReceipt, ExecutionRun, MuxoTool, ProviderLane, ProvisionedResource, RiskLevel, SupplierSettlementSummary, Workspace, WorkspaceMemoryRecord } from "./types.js";
1
+ import type { ActionPlan, ByokKeyRecord, ConnectionRecord, ExecutionReceipt, ExecutionRun, MuxoTool, ProviderLane, ProvisionedResource, RiskLevel, SupplierSettlementSummary, Workspace, WorkspaceMemoryRecord } from "./types.js";
2
2
  export interface MuxoClientOptions {
3
3
  baseUrl?: string;
4
4
  apiKey?: string | (() => string | Promise<string>);
@@ -174,4 +174,80 @@ export declare class MuxoClient {
174
174
  namespace: string;
175
175
  records: WorkspaceMemoryRecord[];
176
176
  }>;
177
+ do(prompt: string, options?: {
178
+ input?: Record<string, unknown>;
179
+ autoApprove?: boolean;
180
+ dryRun?: boolean;
181
+ }): Promise<{
182
+ ok: boolean;
183
+ executed: boolean;
184
+ status: string;
185
+ intent: {
186
+ toolSlug: string | null;
187
+ input: Record<string, unknown>;
188
+ extractedInput: Record<string, unknown>;
189
+ reason: string;
190
+ confidence: number;
191
+ };
192
+ resolution: Record<string, unknown>;
193
+ plan?: ActionPlan;
194
+ run?: ExecutionRun;
195
+ receipt?: ExecutionReceipt;
196
+ humanMessage: string;
197
+ }>;
198
+ ask(prompt: string, options?: {
199
+ input?: Record<string, unknown>;
200
+ autoApprove?: boolean;
201
+ dryRun?: boolean;
202
+ }): Promise<{
203
+ ok: boolean;
204
+ executed: boolean;
205
+ status: string;
206
+ intent: {
207
+ toolSlug: string | null;
208
+ input: Record<string, unknown>;
209
+ extractedInput: Record<string, unknown>;
210
+ reason: string;
211
+ confidence: number;
212
+ };
213
+ resolution: Record<string, unknown>;
214
+ plan?: ActionPlan;
215
+ run?: ExecutionRun;
216
+ receipt?: ExecutionReceipt;
217
+ humanMessage: string;
218
+ }>;
219
+ listByokKeys(): Promise<{
220
+ ok: true;
221
+ workspaceId: string;
222
+ keys: ByokKeyRecord[];
223
+ }>;
224
+ setByokKey(provider: string, secretKey: string): Promise<{
225
+ ok: true;
226
+ key: ByokKeyRecord;
227
+ keys: ByokKeyRecord[];
228
+ }>;
229
+ deleteByokKey(provider: string): Promise<{
230
+ ok: true;
231
+ deleted: boolean;
232
+ provider: string;
233
+ keys: ByokKeyRecord[];
234
+ }>;
235
+ upgradePlan(): Promise<{
236
+ ok: true;
237
+ tier: string;
238
+ workspace: Workspace;
239
+ settlementSummary: SupplierSettlementSummary;
240
+ }>;
241
+ createCheckoutSession(kind?: "starter_subscription" | "credit_topup"): Promise<{
242
+ ok: true;
243
+ sessionId: string;
244
+ url: string;
245
+ mode?: string;
246
+ }>;
247
+ verifyCheckoutSession(sessionId: string): Promise<{
248
+ ok: true;
249
+ fulfilled: boolean;
250
+ workspace: Workspace | null;
251
+ kind: string;
252
+ }>;
177
253
  }
package/dist/sdk.js CHANGED
@@ -221,4 +221,50 @@ export class MuxoClient {
221
221
  const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
222
222
  return this.request(`/api/v1/memory${suffix}`);
223
223
  }
224
+ async do(prompt, options = {}) {
225
+ return this.request("/api/v1/do", {
226
+ method: "POST",
227
+ body: {
228
+ prompt,
229
+ input: options.input,
230
+ autoApprove: options.autoApprove !== false,
231
+ dryRun: options.dryRun === true,
232
+ },
233
+ });
234
+ }
235
+ async ask(prompt, options) {
236
+ return this.do(prompt, options);
237
+ }
238
+ async listByokKeys() {
239
+ return this.request("/api/v1/byok");
240
+ }
241
+ async setByokKey(provider, secretKey) {
242
+ return this.request("/api/v1/byok", {
243
+ method: "POST",
244
+ body: { provider, secretKey },
245
+ });
246
+ }
247
+ async deleteByokKey(provider) {
248
+ return this.request(`/api/v1/byok/${encodeURIComponent(provider)}`, {
249
+ method: "DELETE",
250
+ });
251
+ }
252
+ async upgradePlan() {
253
+ return this.request("/api/v1/billing/upgrade", {
254
+ method: "POST",
255
+ body: {},
256
+ });
257
+ }
258
+ async createCheckoutSession(kind = "credit_topup") {
259
+ return this.request("/api/v1/billing/checkout", {
260
+ method: "POST",
261
+ body: { kind },
262
+ });
263
+ }
264
+ async verifyCheckoutSession(sessionId) {
265
+ return this.request("/api/v1/billing/verify-session", {
266
+ method: "POST",
267
+ body: { sessionId },
268
+ });
269
+ }
224
270
  }
package/dist/server.js CHANGED
@@ -154,7 +154,7 @@ export function createMuxoServer(customConfig) {
154
154
  ok: true,
155
155
  status: "ready",
156
156
  service: "muxo-platform",
157
- version: "1.2.3",
157
+ version: "1.3.0",
158
158
  environment: "production",
159
159
  live_execution: true,
160
160
  customer_billing: true,
@@ -426,7 +426,7 @@ export function createMuxoServer(customConfig) {
426
426
  openapi: "3.1.0",
427
427
  info: {
428
428
  title: "Muxo Unified Agent Execution, Infrastructure, Memory & Governance API",
429
- version: "1.2.3",
429
+ version: "1.3.0",
430
430
  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
431
  },
432
432
  servers: [{ url: config.baseUrl }],
@@ -758,7 +758,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
758
758
  protocolVersion: "2025-06-18",
759
759
  serverInfo: {
760
760
  name: "muxo-mcp-server",
761
- version: "1.2.3",
761
+ version: "1.3.0",
762
762
  },
763
763
  instructions: MUXO_MCP_SERVER_INSTRUCTIONS,
764
764
  toolsCount: MUXO_MCP_TOOLS.length,
@@ -838,6 +838,7 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
838
838
  landingUrl: `${config.baseUrl}/landing`,
839
839
  },
840
840
  apiKeys: store.listApiKeys(ctx.workspace.id),
841
+ byokKeys: store.listByokKeys(ctx.workspace.id),
841
842
  defaultApiKey: bootstrap.defaultApiKey || null,
842
843
  connections: store.listConnections(ctx.workspace.id),
843
844
  resources: store.listResources(ctx.workspace.id),
@@ -1058,7 +1059,91 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1058
1059
  sendJson(res, 200, { ok: true, ...verified });
1059
1060
  return;
1060
1061
  }
1062
+ // BYOK (Bring Your Own Key) Encrypted Vault Management
1063
+ if (pathname === "/api/v1/byok" && req.method === "GET") {
1064
+ sendJson(res, 200, {
1065
+ ok: true,
1066
+ workspaceId: ctx.workspace.id,
1067
+ keys: store.listByokKeys(ctx.workspace.id),
1068
+ });
1069
+ return;
1070
+ }
1071
+ if (pathname === "/api/v1/byok" && req.method === "POST") {
1072
+ const body = JSON.parse((await readBody(req)) || "{}");
1073
+ const provider = String(body.provider || "").trim();
1074
+ const secretKey = String(body.secretKey || body.apiKey || body.key || "").trim();
1075
+ if (!provider || !secretKey) {
1076
+ sendJson(res, 400, {
1077
+ ok: false,
1078
+ error: "Both 'provider' and 'secretKey' are required",
1079
+ });
1080
+ return;
1081
+ }
1082
+ const record = store.setByokKey(ctx.workspace.id, provider, secretKey);
1083
+ sendJson(res, 201, {
1084
+ ok: true,
1085
+ key: record,
1086
+ keys: store.listByokKeys(ctx.workspace.id),
1087
+ });
1088
+ return;
1089
+ }
1090
+ if (pathname.startsWith("/api/v1/byok/") && req.method === "DELETE") {
1091
+ const provider = decodeURIComponent(pathname.slice("/api/v1/byok/".length));
1092
+ const deleted = store.deleteByokKey(ctx.workspace.id, provider);
1093
+ sendJson(res, 200, {
1094
+ ok: true,
1095
+ deleted,
1096
+ provider,
1097
+ keys: store.listByokKeys(ctx.workspace.id),
1098
+ });
1099
+ return;
1100
+ }
1101
+ // Natural Language Intent & Governed Execution Endpoint
1102
+ if ((pathname === "/api/v1/do" || pathname === "/v1/do") &&
1103
+ req.method === "POST") {
1104
+ const body = JSON.parse((await readBody(req)) || "{}");
1105
+ const prompt = String(body.prompt || body.instruction || body.query || body.command || "").trim();
1106
+ if (!prompt) {
1107
+ sendJson(res, 400, {
1108
+ ok: false,
1109
+ error: "Missing 'prompt' or 'instruction' in request body",
1110
+ });
1111
+ return;
1112
+ }
1113
+ const result = await engine.doNaturalLanguage(ctx, {
1114
+ prompt,
1115
+ input: body.input && typeof body.input === "object"
1116
+ ? body.input
1117
+ : undefined,
1118
+ autoApprove: body.autoApprove !== false,
1119
+ dryRun: body.dryRun === true,
1120
+ });
1121
+ sendJson(res, 200, result);
1122
+ return;
1123
+ }
1061
1124
  // Live Stripe Billing, Credit Top-Up & Supplier Settlement Ledger
1125
+ if (pathname === "/api/v1/billing/status" && req.method === "GET") {
1126
+ const ws = store.getWorkspace(ctx.workspace.id);
1127
+ const settlementSummary = store.getSupplierSettlementSummary(ctx.workspace.id);
1128
+ const byokKeys = store.listByokKeys(ctx.workspace.id);
1129
+ sendJson(res, 200, {
1130
+ ok: true,
1131
+ workspaceId: ws.id,
1132
+ workspace: ws,
1133
+ tier: ws.tier,
1134
+ creditsBalance: ws.creditsBalance,
1135
+ creditBalance: ws.creditsBalance,
1136
+ monthlyActionsUsed: ws.monthlyActionsUsed,
1137
+ monthlyActionsLimit: ws.monthlyActionsLimit,
1138
+ stripeConfigured: engine.stripeBilling.isConfigured,
1139
+ stripeCustomerId: ws.stripeCustomerId,
1140
+ stripeSubscriptionId: ws.stripeSubscriptionId,
1141
+ settlementSummary,
1142
+ byokKeys,
1143
+ entries: store.listLedgerEntries(ctx.workspace.id, 25),
1144
+ });
1145
+ return;
1146
+ }
1062
1147
  if (pathname === "/api/v1/billing/ledger" && req.method === "GET") {
1063
1148
  sendJson(res, 200, {
1064
1149
  ok: true,
@@ -1097,21 +1182,51 @@ Users purchase Muxo Credits (\`POST /api/v1/billing/topup\` or \`muxo billing to
1097
1182
  });
1098
1183
  return;
1099
1184
  }
1185
+ if (pathname === "/api/v1/billing/upgrade" && req.method === "POST") {
1186
+ const body = JSON.parse((await readBody(req)) || "{}");
1187
+ const referenceId = String(body.referenceId || "").trim() ||
1188
+ `upgrade_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
1189
+ const bonusCredits = body.bonusCredits !== undefined ? Number(body.bonusCredits) : 1000;
1190
+ const updatedWorkspace = store.upgradeWorkspaceToStarter({
1191
+ workspaceId: ctx.workspace.id,
1192
+ stripeCustomerId: ctx.workspace.stripeCustomerId ||
1193
+ `cus_muxo_${ctx.workspace.id.slice(3, 11)}`,
1194
+ stripeSubscriptionId: ctx.workspace.stripeSubscriptionId ||
1195
+ `sub_muxo_${referenceId.slice(-10)}`,
1196
+ referenceId,
1197
+ bonusCredits,
1198
+ });
1199
+ sendJson(res, 200, {
1200
+ ok: true,
1201
+ tier: updatedWorkspace.tier,
1202
+ creditsBalance: updatedWorkspace.creditsBalance,
1203
+ creditBalance: updatedWorkspace.creditsBalance,
1204
+ workspace: updatedWorkspace,
1205
+ settlementSummary: store.getSupplierSettlementSummary(ctx.workspace.id),
1206
+ });
1207
+ return;
1208
+ }
1100
1209
  if (pathname === "/api/v1/billing/checkout" && req.method === "POST") {
1101
1210
  const body = JSON.parse((await readBody(req)) || "{}");
1102
1211
  const kind = body.kind === "credit_topup" ? "credit_topup" : "starter_subscription";
1212
+ const credits = body.credits ? Number(body.credits) : 1000;
1103
1213
  const session = await engine.stripeBilling.createCheckoutSession({
1104
1214
  workspace: ctx.workspace,
1105
1215
  customerEmail: ctx.user.email,
1106
1216
  kind,
1107
- returnBaseUrl: config.baseUrl,
1217
+ credits,
1218
+ returnBaseUrl: body.returnBaseUrl ? String(body.returnBaseUrl) : config.baseUrl,
1108
1219
  });
1109
1220
  sendJson(res, 200, { ok: true, ...session });
1110
1221
  return;
1111
1222
  }
1112
1223
  if (pathname === "/api/v1/billing/verify-session" && req.method === "POST") {
1113
1224
  const body = JSON.parse((await readBody(req)) || "{}");
1114
- const verified = await engine.stripeBilling.verifyAndFulfillCheckoutSession(String(body.sessionId || ""));
1225
+ const verified = await engine.stripeBilling.verifyAndFulfillCheckoutSession(String(body.sessionId || ""), ctx.workspace.id, body.kind === "credit_topup"
1226
+ ? "credit_topup"
1227
+ : body.kind === "starter_subscription"
1228
+ ? "starter_subscription"
1229
+ : undefined);
1115
1230
  sendJson(res, 200, { ok: true, ...verified });
1116
1231
  return;
1117
1232
  }