@muxoai/platform 1.3.3 → 1.4.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.
- package/README.md +6 -4
- package/dist/cli.js +76 -3
- package/dist/sdk.d.ts +98 -5
- package/dist/sdk.js +72 -7
- package/dist/types.d.ts +20 -2
- package/dist/ui/console-html.js +108 -12
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Muxo (`@muxoai/platform` / `@muxoai/cli` / `@muxoai/sdk`)
|
|
2
2
|
|
|
3
|
-
Muxo (`v1.
|
|
3
|
+
Muxo (`v1.4.0`) is the unified execution, cloud infrastructure provisioning, contextual memory, and cryptographic governance platform for AI agents and applications. Customers and their agents interact exclusively with **Muxo** — either via structured tool slugs or free-form **Natural Language** (`muxo do`, `POST /api/v1/do`, `client.do()`, and MCP `plan_action`).
|
|
4
4
|
|
|
5
5
|
See [`ARCHITECTURE.md`](./ARCHITECTURE.md) for the complete internal engineering blueprint, open-source ecosystem Keep/Adopt/Drop matrix, and 4-pillar architecture.
|
|
6
6
|
|
|
@@ -37,10 +37,12 @@ muxo byok set openai # Interactive hidden/line prompt if key omitted
|
|
|
37
37
|
muxo byok set composio # Executes app.* and instant.* tools at 0 Muxo credit cost
|
|
38
38
|
muxo byok list
|
|
39
39
|
|
|
40
|
-
# 6. Manage Billing, Credits & Starter
|
|
40
|
+
# 6. Manage Billing, Credits & Plans (Starter $29/mo, Pro $99/mo, yearly 2 months free)
|
|
41
41
|
muxo billing status
|
|
42
|
+
muxo billing plans
|
|
42
43
|
muxo billing topup 1000
|
|
43
|
-
muxo billing upgrade
|
|
44
|
+
muxo billing upgrade --plan pro --yearly
|
|
45
|
+
muxo billing portal
|
|
44
46
|
|
|
45
47
|
# 7. Connect an OAuth SaaS App (browser consent handoff) & Provision Live Cloud Infrastructure
|
|
46
48
|
muxo link gmail --wait
|
|
@@ -115,7 +117,7 @@ Muxo ships a portable OpenAI Plugin (`plugins/muxo/plugin.json` & `plugins/muxo/
|
|
|
115
117
|
| `src/providers/composio-sdk.ts` | Live Composio v3 Connected Apps adapter (catalog search, OAuth sessions/links, tool execution) |
|
|
116
118
|
| `src/providers/composio-instant.ts` | Live Composio Instant adapter (`search`, `scrape`, `crawl`, `seo.audit`, `hackernews`, `github.repo_intel`, `dns.lookup`, `workbench`) |
|
|
117
119
|
| `src/providers/stripe-projects.ts` | Live Stripe Projects CLI bridge (`status`, `catalog`, `add`, `rotate`, `remove`, `env --pull`) |
|
|
118
|
-
| `src/providers/stripe-billing.ts` | Live Stripe Checkout billing (`$29/mo
|
|
120
|
+
| `src/providers/stripe-billing.ts` | Live Stripe Checkout billing (Starter `$29/mo`, Pro `$99/mo`, yearly billing & credit packs with volume discounts) |
|
|
119
121
|
| `src/providers/microsoft-governance.ts` | Microsoft Agent Governance Toolkit v5.0.0 (`McpSecurityScanner`, `PolicyEngine`, `AgentIdentity` Ed25519 DID, `AuditLogger`) |
|
|
120
122
|
| `src/providers/supermemory.ts` | Supermemory v5 Contextual Brain (`profile`, hybrid `search`, secret-redacted `add`, `delete`) |
|
|
121
123
|
| `src/engine.ts` | Unified Governance, Memory & Execution Engine implementing all 19 canonical MCP operations |
|
package/dist/cli.js
CHANGED
|
@@ -113,6 +113,9 @@ async function runCli() {
|
|
|
113
113
|
if (opts?.acceptPaymentRequired && res.status === 402 && data.checkoutUrl) {
|
|
114
114
|
return data;
|
|
115
115
|
}
|
|
116
|
+
if (opts?.acceptHumanRequired && res.status === 403 && data.code === "human_required") {
|
|
117
|
+
return data;
|
|
118
|
+
}
|
|
116
119
|
if (!res.ok && data.error) {
|
|
117
120
|
throw new Error(String(data.error));
|
|
118
121
|
}
|
|
@@ -177,6 +180,27 @@ async function runCli() {
|
|
|
177
180
|
const maskKey = (key) => `${key.slice(0, 14)}…`;
|
|
178
181
|
// Hosted Muxo answers purchases with 402 + a Stripe Checkout URL; a self-hosted dev server may grant credits directly.
|
|
179
182
|
const printBillingResult = (data) => {
|
|
183
|
+
if (data.code === "human_required" || data.status === "human_required") {
|
|
184
|
+
console.log(JSON.stringify({
|
|
185
|
+
ok: false,
|
|
186
|
+
humanRequired: true,
|
|
187
|
+
url: data.url,
|
|
188
|
+
error: data.error,
|
|
189
|
+
next: "Open the url in a browser and sign in to the Muxo console to finish this step. Agent API keys cannot manage billing or approvals.",
|
|
190
|
+
}, null, 2));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (data.portalUrl) {
|
|
194
|
+
console.log(JSON.stringify({
|
|
195
|
+
ok: true,
|
|
196
|
+
actionRequired: true,
|
|
197
|
+
tier: data.tier,
|
|
198
|
+
interval: data.interval,
|
|
199
|
+
portalUrl: data.portalUrl,
|
|
200
|
+
next: data.nextStep || "Open portalUrl in a browser; a signed-in person confirms the plan change in the Stripe billing portal.",
|
|
201
|
+
}, null, 2));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
180
204
|
if (!data.checkoutUrl) {
|
|
181
205
|
console.log(JSON.stringify(data, null, 2));
|
|
182
206
|
return;
|
|
@@ -998,7 +1022,18 @@ async function runCli() {
|
|
|
998
1022
|
if (!planId)
|
|
999
1023
|
throw new Error("Usage: muxo approve <plan_id>");
|
|
1000
1024
|
if (remoteUrl) {
|
|
1001
|
-
const approved = await remoteFetch(`/api/v1/plans/${encodeURIComponent(planId)}/approve`, { method: "POST", body: {} });
|
|
1025
|
+
const approved = await remoteFetch(`/api/v1/plans/${encodeURIComponent(planId)}/approve`, { method: "POST", body: {}, acceptHumanRequired: true });
|
|
1026
|
+
if (approved.code === "human_required") {
|
|
1027
|
+
console.log(JSON.stringify({
|
|
1028
|
+
ok: false,
|
|
1029
|
+
humanRequired: true,
|
|
1030
|
+
planId,
|
|
1031
|
+
approvalUrl: approved.url,
|
|
1032
|
+
error: approved.error,
|
|
1033
|
+
hint: "Open approvalUrl in a browser; a signed-in person approves the plan in the Muxo console. Agent API keys cannot approve plans.",
|
|
1034
|
+
}, null, 2));
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1002
1037
|
console.log(JSON.stringify(approved.plan || approved, null, 2));
|
|
1003
1038
|
return;
|
|
1004
1039
|
}
|
|
@@ -1050,7 +1085,18 @@ async function runCli() {
|
|
|
1050
1085
|
const plan = planned.plan || planned;
|
|
1051
1086
|
if (plan.approvalStatus === "pending_approval") {
|
|
1052
1087
|
if (flags.approve || cmd === "provision") {
|
|
1053
|
-
await remoteFetch(`/api/v1/plans/${encodeURIComponent(plan.id)}/approve`, { method: "POST", body: {} });
|
|
1088
|
+
const approved = await remoteFetch(`/api/v1/plans/${encodeURIComponent(plan.id)}/approve`, { method: "POST", body: {}, acceptHumanRequired: true });
|
|
1089
|
+
if (approved.code === "human_required") {
|
|
1090
|
+
console.log(JSON.stringify({
|
|
1091
|
+
ok: false,
|
|
1092
|
+
status: "pending_approval",
|
|
1093
|
+
humanRequired: true,
|
|
1094
|
+
plan,
|
|
1095
|
+
approvalUrl: approved.url,
|
|
1096
|
+
hint: "This plan needs approval from a signed-in person. Open approvalUrl in a browser to approve it in the Muxo console; agent API keys cannot approve plans.",
|
|
1097
|
+
}, null, 2));
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1054
1100
|
}
|
|
1055
1101
|
else {
|
|
1056
1102
|
console.log(JSON.stringify({
|
|
@@ -1230,11 +1276,17 @@ async function runCli() {
|
|
|
1230
1276
|
return;
|
|
1231
1277
|
}
|
|
1232
1278
|
if (sub === "upgrade") {
|
|
1279
|
+
const planArg = String(flags.plan || positional[2] || "starter").toLowerCase();
|
|
1280
|
+
const tier = planArg === "pro" ? "pro" : "starter";
|
|
1281
|
+
const interval = flags.yearly || flags.year || String(flags.interval || "").startsWith("y")
|
|
1282
|
+
? "year"
|
|
1283
|
+
: "month";
|
|
1233
1284
|
if (remoteUrl) {
|
|
1234
1285
|
const data = await remoteFetch("/api/v1/billing/upgrade", {
|
|
1235
1286
|
method: "POST",
|
|
1236
|
-
body: { tier
|
|
1287
|
+
body: { tier, interval },
|
|
1237
1288
|
acceptPaymentRequired: true,
|
|
1289
|
+
acceptHumanRequired: true,
|
|
1238
1290
|
});
|
|
1239
1291
|
printBillingResult(data);
|
|
1240
1292
|
return;
|
|
@@ -1282,6 +1334,27 @@ async function runCli() {
|
|
|
1282
1334
|
console.log(JSON.stringify({ ok: true, kind, ...session }, null, 2));
|
|
1283
1335
|
return;
|
|
1284
1336
|
}
|
|
1337
|
+
if (sub === "plans") {
|
|
1338
|
+
const plansUrl = remoteUrl || config.baseUrl;
|
|
1339
|
+
const res = await fetch(`${plansUrl}/api/v1/billing/plans`, {
|
|
1340
|
+
headers: { Accept: "application/json" },
|
|
1341
|
+
});
|
|
1342
|
+
const data = (await res.json().catch(() => ({})));
|
|
1343
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
if (sub === "portal") {
|
|
1347
|
+
if (remoteUrl) {
|
|
1348
|
+
const data = await remoteFetch("/api/v1/billing/portal", {
|
|
1349
|
+
method: "POST",
|
|
1350
|
+
body: {},
|
|
1351
|
+
acceptHumanRequired: true,
|
|
1352
|
+
});
|
|
1353
|
+
printBillingResult(data);
|
|
1354
|
+
return;
|
|
1355
|
+
}
|
|
1356
|
+
throw new Error("muxo billing portal is only available for the hosted Muxo service (set --remote or log in first).");
|
|
1357
|
+
}
|
|
1285
1358
|
if (sub === "verify") {
|
|
1286
1359
|
const sessionId = positional[2] || String(flags.session || "");
|
|
1287
1360
|
if (!sessionId)
|
package/dist/sdk.d.ts
CHANGED
|
@@ -11,6 +11,77 @@ export interface MuxoClientOptions {
|
|
|
11
11
|
fetch?: typeof fetch;
|
|
12
12
|
timeoutMs?: number;
|
|
13
13
|
}
|
|
14
|
+
/** Error thrown for any non-OK Muxo API response. Carries the machine-readable code and payload. */
|
|
15
|
+
export declare class MuxoApiError extends Error {
|
|
16
|
+
readonly status: number;
|
|
17
|
+
readonly code?: string;
|
|
18
|
+
readonly url?: string;
|
|
19
|
+
readonly checkoutUrl?: string;
|
|
20
|
+
readonly data: Record<string, unknown>;
|
|
21
|
+
constructor(status: number, payload: Record<string, unknown>);
|
|
22
|
+
}
|
|
23
|
+
export interface BillingPlanInfo {
|
|
24
|
+
tier: "free" | "starter" | "pro";
|
|
25
|
+
name: string;
|
|
26
|
+
tagline: string;
|
|
27
|
+
monthlyActions: number;
|
|
28
|
+
monthlyCredits: number;
|
|
29
|
+
prices: {
|
|
30
|
+
month: {
|
|
31
|
+
cents: number;
|
|
32
|
+
label: string;
|
|
33
|
+
};
|
|
34
|
+
year: {
|
|
35
|
+
cents: number;
|
|
36
|
+
label: string;
|
|
37
|
+
perMonthLabel: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
features: string[];
|
|
41
|
+
}
|
|
42
|
+
export interface PlanCatalog {
|
|
43
|
+
plans: BillingPlanInfo[];
|
|
44
|
+
yearlyDiscount: string;
|
|
45
|
+
creditPriceCents: number;
|
|
46
|
+
creditPacks: Array<{
|
|
47
|
+
credits: number;
|
|
48
|
+
cents: number;
|
|
49
|
+
label: string;
|
|
50
|
+
}>;
|
|
51
|
+
minCheckoutCredits: number;
|
|
52
|
+
}
|
|
53
|
+
export type UpgradePlanResult = {
|
|
54
|
+
ok: true;
|
|
55
|
+
unchanged: true;
|
|
56
|
+
tier: string;
|
|
57
|
+
message: string;
|
|
58
|
+
workspace?: Workspace;
|
|
59
|
+
} | {
|
|
60
|
+
ok: true;
|
|
61
|
+
mode: "portal";
|
|
62
|
+
action: "billing_portal";
|
|
63
|
+
tier: string;
|
|
64
|
+
interval: string;
|
|
65
|
+
portalUrl: string;
|
|
66
|
+
url: string;
|
|
67
|
+
nextStep: string;
|
|
68
|
+
} | {
|
|
69
|
+
ok: false;
|
|
70
|
+
code: "payment_required";
|
|
71
|
+
paymentRequired: true;
|
|
72
|
+
checkoutUrl: string;
|
|
73
|
+
sessionId: string;
|
|
74
|
+
kind: string;
|
|
75
|
+
tier: string;
|
|
76
|
+
interval: string;
|
|
77
|
+
credits: number;
|
|
78
|
+
error: string;
|
|
79
|
+
} | {
|
|
80
|
+
ok: false;
|
|
81
|
+
code: "human_required";
|
|
82
|
+
url: string;
|
|
83
|
+
error: string;
|
|
84
|
+
};
|
|
14
85
|
export declare class MuxoClient {
|
|
15
86
|
readonly baseUrl: string;
|
|
16
87
|
private readonly apiKeyResolver?;
|
|
@@ -398,19 +469,41 @@ export declare class MuxoClient {
|
|
|
398
469
|
provider: string;
|
|
399
470
|
keys: ByokKeyRecord[];
|
|
400
471
|
}>;
|
|
401
|
-
|
|
472
|
+
/** Lists plans, prices, and credit packs. No auth required. */
|
|
473
|
+
getPlans(): Promise<{
|
|
474
|
+
ok: true;
|
|
475
|
+
} & PlanCatalog>;
|
|
476
|
+
/**
|
|
477
|
+
* Moves the workspace to a plan. A first subscription returns a Stripe checkout URL
|
|
478
|
+
* (`payment_required`); changing an existing subscription returns a billing-portal URL.
|
|
479
|
+
* Plan changes are confirmed by a signed-in person; API keys get `human_required`.
|
|
480
|
+
*/
|
|
481
|
+
upgradePlan(options?: {
|
|
482
|
+
tier?: "starter" | "pro";
|
|
483
|
+
interval?: "month" | "year";
|
|
484
|
+
}): Promise<UpgradePlanResult>;
|
|
485
|
+
/**
|
|
486
|
+
* Opens the Stripe billing portal to manage payment details, switch plans, or cancel.
|
|
487
|
+
* Signed-in people only; API keys get a `human_required` MuxoApiError.
|
|
488
|
+
*/
|
|
489
|
+
openBillingPortal(): Promise<{
|
|
402
490
|
ok: true;
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
settlementSummary: SupplierSettlementSummary;
|
|
491
|
+
url: string;
|
|
492
|
+
portalUrl: string;
|
|
406
493
|
}>;
|
|
407
|
-
createCheckoutSession(kind?: "starter_subscription" | "credit_topup",
|
|
494
|
+
createCheckoutSession(kind?: "subscription" | "starter_subscription" | "credit_topup", optionsOrCredits?: number | {
|
|
495
|
+
credits?: number;
|
|
496
|
+
tier?: "starter" | "pro";
|
|
497
|
+
interval?: "month" | "year";
|
|
498
|
+
}): Promise<{
|
|
408
499
|
ok: true;
|
|
409
500
|
sessionId: string;
|
|
410
501
|
url: string;
|
|
411
502
|
mode?: string;
|
|
412
503
|
kind?: string;
|
|
413
504
|
credits?: number;
|
|
505
|
+
tier?: string;
|
|
506
|
+
interval?: string;
|
|
414
507
|
}>;
|
|
415
508
|
verifyCheckoutSession(sessionId: string): Promise<{
|
|
416
509
|
ok: true;
|
package/dist/sdk.js
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/** Error thrown for any non-OK Muxo API response. Carries the machine-readable code and payload. */
|
|
2
|
+
export class MuxoApiError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
code;
|
|
5
|
+
url;
|
|
6
|
+
checkoutUrl;
|
|
7
|
+
data;
|
|
8
|
+
constructor(status, payload) {
|
|
9
|
+
super(String(payload.error || `Muxo request failed with HTTP ${status}`));
|
|
10
|
+
this.name = "MuxoApiError";
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.code = typeof payload.code === "string" ? payload.code : undefined;
|
|
13
|
+
this.url = typeof payload.url === "string" ? payload.url : undefined;
|
|
14
|
+
this.checkoutUrl =
|
|
15
|
+
typeof payload.checkoutUrl === "string" ? payload.checkoutUrl : undefined;
|
|
16
|
+
this.data = payload;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
1
19
|
export class MuxoClient {
|
|
2
20
|
baseUrl;
|
|
3
21
|
apiKeyResolver;
|
|
@@ -41,7 +59,7 @@ export class MuxoClient {
|
|
|
41
59
|
});
|
|
42
60
|
const payload = (await res.json());
|
|
43
61
|
if (!res.ok || payload.ok === false) {
|
|
44
|
-
throw new
|
|
62
|
+
throw new MuxoApiError(res.status, payload);
|
|
45
63
|
}
|
|
46
64
|
return payload;
|
|
47
65
|
}
|
|
@@ -166,8 +184,21 @@ export class MuxoClient {
|
|
|
166
184
|
});
|
|
167
185
|
let plan = planned.plan;
|
|
168
186
|
if (plan.approvalStatus === "pending_approval" && options.approve) {
|
|
169
|
-
|
|
170
|
-
|
|
187
|
+
try {
|
|
188
|
+
const approved = await this.approvePlan(plan.id);
|
|
189
|
+
plan = approved.plan;
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
if (err instanceof MuxoApiError && err.code === "human_required") {
|
|
193
|
+
throw new MuxoApiError(err.status, {
|
|
194
|
+
...err.data,
|
|
195
|
+
planId: plan.id,
|
|
196
|
+
approvalUrl: err.url,
|
|
197
|
+
error: `Plan ${plan.id} needs approval from a signed-in person at ${err.url || "the Muxo console"}; API keys cannot approve plans.`,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
throw err;
|
|
201
|
+
}
|
|
171
202
|
}
|
|
172
203
|
const executed = await this.executePlan(plan.id);
|
|
173
204
|
return {
|
|
@@ -360,16 +391,50 @@ export class MuxoClient {
|
|
|
360
391
|
method: "DELETE",
|
|
361
392
|
});
|
|
362
393
|
}
|
|
363
|
-
|
|
364
|
-
|
|
394
|
+
/** Lists plans, prices, and credit packs. No auth required. */
|
|
395
|
+
async getPlans() {
|
|
396
|
+
return this.request("/api/v1/billing/plans");
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Moves the workspace to a plan. A first subscription returns a Stripe checkout URL
|
|
400
|
+
* (`payment_required`); changing an existing subscription returns a billing-portal URL.
|
|
401
|
+
* Plan changes are confirmed by a signed-in person; API keys get `human_required`.
|
|
402
|
+
*/
|
|
403
|
+
async upgradePlan(options = {}) {
|
|
404
|
+
try {
|
|
405
|
+
return await this.request("/api/v1/billing/upgrade", {
|
|
406
|
+
method: "POST",
|
|
407
|
+
body: {
|
|
408
|
+
tier: options.tier || "starter",
|
|
409
|
+
interval: options.interval || "month",
|
|
410
|
+
},
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
catch (err) {
|
|
414
|
+
if (err instanceof MuxoApiError &&
|
|
415
|
+
(err.code === "payment_required" || err.code === "human_required")) {
|
|
416
|
+
return err.data;
|
|
417
|
+
}
|
|
418
|
+
throw err;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Opens the Stripe billing portal to manage payment details, switch plans, or cancel.
|
|
423
|
+
* Signed-in people only; API keys get a `human_required` MuxoApiError.
|
|
424
|
+
*/
|
|
425
|
+
async openBillingPortal() {
|
|
426
|
+
return this.request("/api/v1/billing/portal", {
|
|
365
427
|
method: "POST",
|
|
366
428
|
body: {},
|
|
367
429
|
});
|
|
368
430
|
}
|
|
369
|
-
async createCheckoutSession(kind = "credit_topup",
|
|
431
|
+
async createCheckoutSession(kind = "credit_topup", optionsOrCredits) {
|
|
432
|
+
const opts = typeof optionsOrCredits === "number"
|
|
433
|
+
? { credits: optionsOrCredits }
|
|
434
|
+
: optionsOrCredits || {};
|
|
370
435
|
return this.request("/api/v1/billing/checkout", {
|
|
371
436
|
method: "POST",
|
|
372
|
-
body:
|
|
437
|
+
body: { kind, ...opts },
|
|
373
438
|
});
|
|
374
439
|
}
|
|
375
440
|
async verifyCheckoutSession(sessionId) {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
export type RiskLevel = "read" | "write" | "admin" | "financial" | "provisioning" | "destructive";
|
|
2
2
|
export type ProviderLane = "composio_sdk" | "composio_instant" | "stripe_projects";
|
|
3
|
-
export type SubscriptionTier = "free" | "starter";
|
|
3
|
+
export type SubscriptionTier = "free" | "starter" | "pro";
|
|
4
|
+
export type SubscriptionStatus = "none" | "active" | "trialing" | "past_due" | "unpaid" | "canceled" | "incomplete" | "comped";
|
|
5
|
+
export interface WorkspaceBilling {
|
|
6
|
+
interval: "month" | "year" | null;
|
|
7
|
+
status: SubscriptionStatus;
|
|
8
|
+
cancelAtPeriodEnd: boolean;
|
|
9
|
+
/** When Stripe next renews or ends the subscription. */
|
|
10
|
+
currentPeriodEnd: string | null;
|
|
11
|
+
/** Monthly usage periods are counted from this moment. */
|
|
12
|
+
usagePeriodAnchor: string;
|
|
13
|
+
usagePeriodStart: string;
|
|
14
|
+
usagePeriodEnd: string;
|
|
15
|
+
/** usagePeriodStart of the last period whose plan credits were granted. */
|
|
16
|
+
creditsGrantedFor: string | null;
|
|
17
|
+
paymentFailedAt?: string | null;
|
|
18
|
+
/** Subscription whose start (fresh usage period and first credits) was already applied. */
|
|
19
|
+
startedSubscriptionId?: string | null;
|
|
20
|
+
}
|
|
4
21
|
export interface GovernancePolicy {
|
|
5
22
|
mode: "monitor" | "enforce";
|
|
6
23
|
requireApprovalFor: RiskLevel[];
|
|
@@ -32,6 +49,7 @@ export interface Workspace {
|
|
|
32
49
|
creditsBalance: number;
|
|
33
50
|
stripeCustomerId: string | null;
|
|
34
51
|
stripeSubscriptionId: string | null;
|
|
52
|
+
billing?: WorkspaceBilling;
|
|
35
53
|
projectsEnvName: string;
|
|
36
54
|
policy: GovernancePolicy;
|
|
37
55
|
createdAt: string;
|
|
@@ -360,7 +378,7 @@ export interface ExecutionReceipt {
|
|
|
360
378
|
export interface LedgerEntry {
|
|
361
379
|
id: string;
|
|
362
380
|
workspaceId: string;
|
|
363
|
-
kind: "plan_grant" | "credit_topup" | "run_deduction" | "subscription_upgrade" | "manual_adjustment";
|
|
381
|
+
kind: "plan_grant" | "credit_topup" | "run_deduction" | "subscription_upgrade" | "plan_change" | "refund" | "manual_adjustment";
|
|
364
382
|
actionsDelta: number;
|
|
365
383
|
creditsDelta: number;
|
|
366
384
|
balanceAfter: number;
|
package/dist/ui/console-html.js
CHANGED
|
@@ -267,7 +267,7 @@ var LANE_LABELS = { composio_instant: 'Instant tool', composio_sdk: 'Connected a
|
|
|
267
267
|
var TOKEN_KEY = 'muxo_external_api_key';
|
|
268
268
|
var ESC = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
|
269
269
|
|
|
270
|
-
var state = { view: 'overview', boot: null, harness: 'plugin', lane: 'all', factsMode: 'current', facts: null, authMode: 'login' };
|
|
270
|
+
var state = { view: 'overview', boot: null, harness: 'plugin', lane: 'all', factsMode: 'current', facts: null, authMode: 'login', billingInterval: 'month', planCatalog: null };
|
|
271
271
|
var currentTool = null;
|
|
272
272
|
var activeKeyHint = 'mk_live_...';
|
|
273
273
|
var externalApiKey = readToken();
|
|
@@ -472,6 +472,7 @@ async function api(path, opts) {
|
|
|
472
472
|
if (!res.ok || data.ok === false) {
|
|
473
473
|
var err = new Error(data.error || data.message || ('HTTP ' + res.status));
|
|
474
474
|
err.status = res.status;
|
|
475
|
+
err.data = data;
|
|
475
476
|
if (res.status === 401 && !opts.anonymous) handleUnauthorized();
|
|
476
477
|
throw err;
|
|
477
478
|
}
|
|
@@ -1248,10 +1249,19 @@ async function loadPlans() {
|
|
|
1248
1249
|
: (p.approvalStatus === 'approved' || p.approvalStatus === 'auto_approved')
|
|
1249
1250
|
? '<button class="btn btn-sm" data-action="execute-plan" data-id="' + id + '">Run</button>'
|
|
1250
1251
|
: '<span class="mono small muted">' + esc(p.runId ? shortId(p.runId) : 'Done') + '</span>';
|
|
1251
|
-
return '<tr><td class="mono small">' + esc(shortId(p.id)) + '</td><td class="mono small">' + esc(p.toolSlug) + '</td>' +
|
|
1252
|
+
return '<tr id="plan-row-' + id + '"><td class="mono small">' + esc(shortId(p.id)) + '</td><td class="mono small">' + esc(p.toolSlug) + '</td>' +
|
|
1252
1253
|
'<td>' + riskBadge(p.riskLevel) + ' ' + pill(verdict, verdict === 'deny' ? 'danger' : '') + '</td>' +
|
|
1253
1254
|
'<td>' + statusBadge(p.approvalStatus) + '</td><td class="cell-actions">' + action + '</td></tr>';
|
|
1254
1255
|
}).join('') : emptyRow(5, 'Nothing waiting. Plans appear here when an agent asks to run a tool.');
|
|
1256
|
+
var focusPlan = new URLSearchParams(window.location.search).get('plan');
|
|
1257
|
+
if (focusPlan && /^[A-Za-z0-9_-]+$/.test(focusPlan)) {
|
|
1258
|
+
var row = byId('plan-row-' + focusPlan);
|
|
1259
|
+
if (row) {
|
|
1260
|
+
row.style.background = 'rgba(99, 102, 241, 0.14)';
|
|
1261
|
+
row.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
1262
|
+
showToast('This plan is waiting for your approval.');
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1255
1265
|
}
|
|
1256
1266
|
async function approveAndRun(planId) {
|
|
1257
1267
|
await post('/api/v1/plans/' + planId + '/approve');
|
|
@@ -1349,6 +1359,7 @@ async function loadLedger() {
|
|
|
1349
1359
|
'<td class="mono" style="color:' + (delta >= 0 ? 'var(--color-ok)' : 'var(--color-text)') + '">' + (delta >= 0 ? '+' : '') + esc(fmt(delta)) + '</td>' +
|
|
1350
1360
|
'<td class="mono">' + esc(fmt(e.balanceAfter)) + '</td></tr>';
|
|
1351
1361
|
}).join('') : emptyRow(4, 'No billing activity yet.');
|
|
1362
|
+
await renderPlanCards();
|
|
1352
1363
|
await loadByokKeys();
|
|
1353
1364
|
}
|
|
1354
1365
|
async function loadByokKeys() {
|
|
@@ -1426,6 +1437,85 @@ async function startCheckout(kind) {
|
|
|
1426
1437
|
if (res.url) window.location.href = res.url;
|
|
1427
1438
|
}
|
|
1428
1439
|
|
|
1440
|
+
var FALLBACK_PLANS = [
|
|
1441
|
+
{ tier: 'free', name: 'Free', tagline: 'Try Muxo with your first agent.', monthlyActions: 500, monthlyCredits: 0, prices: { month: { cents: 0, label: '$0' }, year: { cents: 0, label: '$0', perMonthLabel: '$0' } }, features: ['500 governed actions a month', '250 trial credits', 'Approvals, audit log, and workspace memory', 'Bring your own provider keys'] },
|
|
1442
|
+
{ tier: 'starter', name: 'Starter', tagline: 'For builders running agents every day.', monthlyActions: 5000, monthlyCredits: 1000, prices: { month: { cents: 2900, label: '$29' }, year: { cents: 29000, label: '$290', perMonthLabel: '$24' } }, features: ['5,000 governed actions a month', '1,000 credits every month', 'Everything in Free', 'Email support'] },
|
|
1443
|
+
{ tier: 'pro', name: 'Pro', tagline: 'For teams with agents in production.', monthlyActions: 25000, monthlyCredits: 5000, prices: { month: { cents: 9900, label: '$99' }, year: { cents: 99000, label: '$990', perMonthLabel: '$82.50' } }, features: ['25,000 governed actions a month', '5,000 credits every month', 'Everything in Starter', 'Priority support'] }
|
|
1444
|
+
];
|
|
1445
|
+
var TIER_ORDER = { free: 0, starter: 1, pro: 2 };
|
|
1446
|
+
|
|
1447
|
+
async function planCatalog() {
|
|
1448
|
+
if (!state.planCatalog) {
|
|
1449
|
+
try { state.planCatalog = await api('/api/v1/billing/plans', { anonymous: true }); } catch (e) { state.planCatalog = { plans: FALLBACK_PLANS }; }
|
|
1450
|
+
}
|
|
1451
|
+
return state.planCatalog;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
async function renderPlanCards() {
|
|
1455
|
+
var box = byId('plan-cards');
|
|
1456
|
+
if (!box) return;
|
|
1457
|
+
var catalog = await planCatalog();
|
|
1458
|
+
var org = (state.boot && state.boot.organization) || {};
|
|
1459
|
+
var billing = (state.boot && state.boot.billing) || {};
|
|
1460
|
+
var tier = String(org.tier || 'free');
|
|
1461
|
+
var interval = state.billingInterval;
|
|
1462
|
+
box.innerHTML = (catalog.plans || FALLBACK_PLANS).map(function (p) {
|
|
1463
|
+
var current = tier === p.tier;
|
|
1464
|
+
var price = p.prices[interval] || p.prices.month;
|
|
1465
|
+
var priceLine = p.tier === 'free'
|
|
1466
|
+
? '<p class="price">$0 <span>forever</span></p>'
|
|
1467
|
+
: '<p class="price">' + esc(price.label) + ' <span>' + (interval === 'year' ? 'per year · ' + esc(price.perMonthLabel) + '/mo' : 'per month') + '</span></p>';
|
|
1468
|
+
var action;
|
|
1469
|
+
if (current) action = pill('Current plan', 'ok');
|
|
1470
|
+
else if (TIER_ORDER[p.tier] > TIER_ORDER[tier]) action = '<button class="btn btn-primary" type="button" data-action="change-plan" data-tier="' + esc(p.tier) + '">Upgrade to ' + esc(p.name) + '</button>';
|
|
1471
|
+
else action = '<button class="btn" type="button" data-action="change-plan" data-tier="' + esc(p.tier) + '">Switch to ' + esc(p.name) + '</button>';
|
|
1472
|
+
return '<div class="panel"><div class="panel-header"><div><h2>' + esc(p.name) + '</h2><p>' + esc(p.tagline || '') + '</p></div></div>' +
|
|
1473
|
+
priceLine +
|
|
1474
|
+
'<ul class="plan-list">' + (p.features || []).map(function (f) { return '<li>' + esc(f) + '</li>'; }).join('') + '</ul>' +
|
|
1475
|
+
'<div class="page-actions">' + action + '</div></div>';
|
|
1476
|
+
}).join('');
|
|
1477
|
+
var manage = byId('manage-billing-btn');
|
|
1478
|
+
if (manage) manage.hidden = !billing.canManageBilling;
|
|
1479
|
+
var note = [];
|
|
1480
|
+
if (billing.status === 'comped') note.push('Complimentary plan');
|
|
1481
|
+
else if (billing.status && billing.status !== 'none') note.push('Subscription ' + String(billing.status).replace(/_/g, ' '));
|
|
1482
|
+
if (billing.cancelAtPeriodEnd && billing.renewsAt) note.push('ends ' + String(billing.renewsAt).slice(0, 10));
|
|
1483
|
+
else if (billing.renewsAt && billing.status !== 'comped') note.push('renews ' + String(billing.renewsAt).slice(0, 10));
|
|
1484
|
+
if (billing.paymentFailed) note.push('Payment failed — update your card in Manage billing');
|
|
1485
|
+
if (billing.usage && billing.usage.resetsAt) note.push('Usage resets ' + String(billing.usage.resetsAt).slice(0, 10));
|
|
1486
|
+
setText('billing-status-note', note.join(' · '));
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function setBillingInterval(interval) {
|
|
1490
|
+
state.billingInterval = interval === 'year' ? 'year' : 'month';
|
|
1491
|
+
document.querySelectorAll('[data-action="billing-interval"]').forEach(function (b) {
|
|
1492
|
+
b.setAttribute('aria-pressed', String(b.dataset.interval === state.billingInterval));
|
|
1493
|
+
});
|
|
1494
|
+
renderPlanCards().catch(showError);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
async function changePlan(tier) {
|
|
1498
|
+
showToast('Talking to Stripe…');
|
|
1499
|
+
try {
|
|
1500
|
+
var res = await post('/api/v1/billing/upgrade', { tier: tier, interval: state.billingInterval });
|
|
1501
|
+
if (res.portalUrl || (res.action === 'billing_portal' && res.url)) { window.location.href = res.portalUrl || res.url; return; }
|
|
1502
|
+
if (res.url) { window.location.href = res.url; return; }
|
|
1503
|
+
showToast(res.message || 'Plan is unchanged.');
|
|
1504
|
+
await refreshOverview();
|
|
1505
|
+
await renderPlanCards();
|
|
1506
|
+
} catch (err) {
|
|
1507
|
+
var data = err && err.data;
|
|
1508
|
+
if (data && (data.checkoutUrl || data.url)) { window.location.href = data.checkoutUrl || data.url; return; }
|
|
1509
|
+
showError(err);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
async function manageBilling() {
|
|
1514
|
+
showToast('Opening the Stripe billing portal…');
|
|
1515
|
+
var res = await post('/api/v1/billing/portal', {});
|
|
1516
|
+
if (res.url) window.location.href = res.url;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1429
1519
|
async function checkCheckoutReturn() {
|
|
1430
1520
|
var params = new URLSearchParams(window.location.search);
|
|
1431
1521
|
var clean = false;
|
|
@@ -1442,6 +1532,7 @@ async function checkCheckoutReturn() {
|
|
|
1442
1532
|
}
|
|
1443
1533
|
}
|
|
1444
1534
|
if (params.get('checkout') === 'cancelled') { clean = true; showToast('Checkout cancelled. You were not charged.'); }
|
|
1535
|
+
if (params.get('plan') === 'updated') { clean = true; showToast('Your plan change is confirmed.'); }
|
|
1445
1536
|
if (params.get('connected')) { clean = true; showToast(params.get('connected') + ' is connected'); }
|
|
1446
1537
|
// Keys are never accepted from the URL: a crafted link could sign a person into someone else's workspace.
|
|
1447
1538
|
if (params.get('api_key')) clean = true;
|
|
@@ -1476,6 +1567,9 @@ var ACTIONS = {
|
|
|
1476
1567
|
'delete-memory': function (el) { return deleteMemoryItem(el.dataset.id); },
|
|
1477
1568
|
'facts-mode': function (el) { setFactsMode(el.dataset.mode); },
|
|
1478
1569
|
'approve-plan': function (el) { return approveAndRun(el.dataset.id); },
|
|
1570
|
+
'billing-interval': function (el) { setBillingInterval(el.dataset.interval); },
|
|
1571
|
+
'change-plan': function (el) { return changePlan(el.dataset.tier); },
|
|
1572
|
+
'manage-billing': function () { return manageBilling(); },
|
|
1479
1573
|
'execute-plan': function (el) { return executeExistingPlan(el.dataset.id); },
|
|
1480
1574
|
'kill-resume': resumeKillSwitch,
|
|
1481
1575
|
'refresh-governance': loadPlans,
|
|
@@ -2058,16 +2152,18 @@ export function renderConsoleHtml() {
|
|
|
2058
2152
|
<div class="metric"><p class="metric-label">Paid to providers</p><p class="metric-value" id="billing-wholesale">$0.00</p><p class="metric-sub">Settled by Muxo for you</p></div>
|
|
2059
2153
|
</div>
|
|
2060
2154
|
<div class="split even">
|
|
2061
|
-
<div class="panel">
|
|
2062
|
-
<div class="panel-header"
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
</
|
|
2069
|
-
<div class="
|
|
2070
|
-
|
|
2155
|
+
<div class="panel" style="grid-column:1/-1">
|
|
2156
|
+
<div class="panel-header">
|
|
2157
|
+
<div><h2>Plans</h2><p>Every plan includes approvals, the audit log, and workspace memory. Yearly billing gives you 2 months free.</p></div>
|
|
2158
|
+
<div class="row-actions" role="group" aria-label="Billing period">
|
|
2159
|
+
<button class="chip" type="button" data-action="billing-interval" data-interval="month" aria-pressed="true">Monthly</button>
|
|
2160
|
+
<button class="chip" type="button" data-action="billing-interval" data-interval="year" aria-pressed="false">Yearly · 2 months free</button>
|
|
2161
|
+
</div>
|
|
2162
|
+
</div>
|
|
2163
|
+
<div id="plan-cards" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px"><p class="empty-note">Loading plans…</p></div>
|
|
2164
|
+
<div class="page-actions" style="margin-top:16px;align-items:center">
|
|
2165
|
+
<button class="btn" type="button" data-action="manage-billing" id="manage-billing-btn" hidden>Manage billing in Stripe</button>
|
|
2166
|
+
<small class="muted" id="billing-status-note"></small>
|
|
2071
2167
|
</div>
|
|
2072
2168
|
</div>
|
|
2073
2169
|
<div class="panel">
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MUXO_VERSION = "1.
|
|
1
|
+
export declare const MUXO_VERSION = "1.4.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const MUXO_VERSION = "1.
|
|
1
|
+
export const MUXO_VERSION = "1.4.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muxoai/platform",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Muxo — Unified Execution, Provisioning & Governance Platform over Composio, Stripe Projects, Microsoft Agent Governance & Supermemory",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|