@muxoai/platform 1.2.3 → 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 (41) 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 +72 -28
  5. package/dist/cli.js +1047 -43
  6. package/dist/engine.d.ts +29 -0
  7. package/dist/engine.js +221 -16
  8. package/dist/mcp.d.ts +6 -1
  9. package/dist/mcp.js +120 -26
  10. package/dist/nl.d.ts +34 -0
  11. package/dist/nl.js +601 -0
  12. package/dist/providers/composio-instant.d.ts +1 -0
  13. package/dist/providers/composio-instant.js +6 -5
  14. package/dist/providers/composio-sdk.d.ts +1 -0
  15. package/dist/providers/composio-sdk.js +6 -2
  16. package/dist/providers/stripe-billing.d.ts +13 -3
  17. package/dist/providers/stripe-billing.js +250 -36
  18. package/dist/providers/stripe-projects.js +61 -4
  19. package/dist/providers/supermemory.d.ts +2 -0
  20. package/dist/providers/supermemory.js +58 -40
  21. package/dist/sdk.d.ts +77 -1
  22. package/dist/sdk.js +46 -0
  23. package/dist/server.js +202 -29
  24. package/dist/store.d.ts +19 -2
  25. package/dist/store.js +216 -11
  26. package/dist/types.d.ts +14 -2
  27. package/dist/ui/console-html.js +329 -119
  28. package/package.json +1 -1
  29. package/plugins/muxo/.app.json +1 -1
  30. package/plugins/muxo/.claude-plugin/plugin.json +2 -2
  31. package/plugins/muxo/.codex-plugin/plugin.json +7 -5
  32. package/plugins/muxo/.mcp.json +1 -33
  33. package/plugins/muxo/assets/logo.png +0 -0
  34. package/plugins/muxo/hooks/session-start.sh +1 -1
  35. package/plugins/muxo/plugin.json +63 -6
  36. package/plugins/muxo/skills/muxo/SKILL.md +92 -14
  37. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  38. package/plugins/muxo/skills/muxo/references/cli.md +4 -2
  39. package/plugins/muxo/skills/muxo/references/mcp.md +15 -9
  40. package/plugins/muxo/skills/setup/SKILL.md +8 -2
  41. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
package/dist/cli.js CHANGED
@@ -6,7 +6,20 @@ import readline from "node:readline";
6
6
  import { getConfig } from "./config.js";
7
7
  import { MuxoEngine } from "./engine.js";
8
8
  import { handleJsonRpcMcpRequest } from "./mcp.js";
9
- import { MuxoStore } from "./store.js";
9
+ import { BYOK_PROVIDER_CATALOG, MuxoStore } from "./store.js";
10
+ function promptUserLine(question) {
11
+ return new Promise((resolve) => {
12
+ const rl = readline.createInterface({
13
+ input: process.stdin,
14
+ output: process.stderr,
15
+ });
16
+ rl.question(question, (answer) => {
17
+ rl.close();
18
+ resolve(answer.trim());
19
+ });
20
+ rl.on("close", () => resolve(""));
21
+ });
22
+ }
10
23
  function getCliConfigPath() {
11
24
  if (process.env.MUXO_CONFIG_PATH) {
12
25
  return path.resolve(process.env.MUXO_CONFIG_PATH);
@@ -57,37 +70,104 @@ function parseFlags(args) {
57
70
  return { positional, flags };
58
71
  }
59
72
  async function runCli() {
60
- const config = getConfig();
61
- const store = new MuxoStore(config.dbPath, config.vaultMasterKey);
62
- const engine = new MuxoEngine(config, store);
63
73
  const { positional, flags } = parseFlags(process.argv.slice(2));
64
74
  const cmd = positional[0] || "help";
65
75
  if (flags.version || flags.v || cmd === "version") {
66
76
  console.log(flags.json
67
- ? JSON.stringify({ ok: true, name: "@muxoai/cli", version: "1.2.3" }, null, 2)
68
- : "1.2.3");
77
+ ? JSON.stringify({ ok: true, name: "@muxoai/cli", version: "1.3.1" }, null, 2)
78
+ : "1.3.1");
69
79
  return;
70
80
  }
71
81
  const savedCliConfig = readCliAuthConfig();
72
- const apiKeyFlag = (typeof flags["api-key"] === "string" ? flags["api-key"] : "") ||
82
+ let apiKeyFlag = (typeof flags["api-key"] === "string" ? flags["api-key"] : "") ||
73
83
  process.env.MUXO_API_KEY ||
74
84
  savedCliConfig?.apiKey ||
75
85
  "";
76
- const tokenAuth = apiKeyFlag ? store.resolveAuthToken(apiKeyFlag) : null;
77
- const fallback = store.ensureDefaultWorkspace();
78
- const ctx = tokenAuth
79
- ? {
80
- user: tokenAuth.user,
81
- workspace: tokenAuth.workspace,
82
- actorId: tokenAuth.actorId,
83
- authType: tokenAuth.authType,
84
- }
85
- : {
86
- user: fallback.user,
87
- workspace: fallback.workspace,
88
- actorId: `cli:${fallback.user.id}`,
89
- authType: "session",
86
+ const explicitUrlFlag = (typeof flags.url === "string"
87
+ ? flags.url
88
+ : process.env.MUXO_API_URL || "").replace(/\/+$/, "");
89
+ const useLocalDb = Boolean((process.env.MUXO_DB_PATH || flags.local) && !explicitUrlFlag);
90
+ const remoteUrl = useLocalDb
91
+ ? ""
92
+ : (explicitUrlFlag || savedCliConfig?.apiUrl || "https://api.muxo.ai").replace(/\/+$/, "");
93
+ const remoteFetch = async (pathname, opts) => {
94
+ const keyToUse = opts?.apiKey ?? apiKeyFlag;
95
+ const headers = {
96
+ Accept: "application/json",
90
97
  };
98
+ if (opts?.body !== undefined) {
99
+ headers["Content-Type"] = "application/json";
100
+ }
101
+ if (keyToUse) {
102
+ headers["Authorization"] = `Bearer ${keyToUse}`;
103
+ }
104
+ const res = await fetch(`${remoteUrl}${pathname}`, {
105
+ method: opts?.method || "GET",
106
+ headers,
107
+ body: opts?.body !== undefined ? JSON.stringify(opts.body) : undefined,
108
+ });
109
+ const data = (await res.json().catch(() => ({})));
110
+ if (!res.ok && data.error) {
111
+ throw new Error(String(data.error));
112
+ }
113
+ return data;
114
+ };
115
+ const signupHostedWorkspace = async (agentName, harness) => {
116
+ const createdRemote = await remoteFetch("/api/v1/agents/signup", {
117
+ method: "POST",
118
+ body: { agentName, harness },
119
+ apiKey: "",
120
+ });
121
+ if (!createdRemote || !createdRemote.apiKey) {
122
+ throw new Error(`Hosted signup at ${remoteUrl}/api/v1/agents/signup failed to return an API key.`);
123
+ }
124
+ const authPayload = {
125
+ authenticated: true,
126
+ apiKey: String(createdRemote.apiKey),
127
+ apiUrl: remoteUrl,
128
+ workspaceId: String(createdRemote.workspaceId || createdRemote.workspace?.id || ""),
129
+ workspaceName: String(createdRemote.workspace?.name || `${agentName} Workspace`),
130
+ agentId: String(createdRemote.agentId || createdRemote.user?.id || "usr_agent"),
131
+ governanceDid: String(createdRemote.governanceDid || ""),
132
+ supermemoryNamespace: String(createdRemote.supermemoryNamespace || ""),
133
+ updatedAt: new Date().toISOString(),
134
+ };
135
+ return { authPayload, rawResponse: createdRemote };
136
+ };
137
+ const config = getConfig();
138
+ let _store = null;
139
+ let _engine = null;
140
+ const getStore = () => {
141
+ if (!_store) {
142
+ _store = new MuxoStore(config.dbPath, config.vaultMasterKey);
143
+ }
144
+ return _store;
145
+ };
146
+ const getEngine = () => {
147
+ if (!_engine) {
148
+ _engine = new MuxoEngine(config, getStore());
149
+ }
150
+ return _engine;
151
+ };
152
+ const getLocalContext = () => {
153
+ const store = getStore();
154
+ const tokenAuth = apiKeyFlag ? store.resolveAuthToken(apiKeyFlag) : null;
155
+ const fallback = store.ensureDefaultWorkspace();
156
+ const ctx = tokenAuth
157
+ ? {
158
+ user: tokenAuth.user,
159
+ workspace: tokenAuth.workspace,
160
+ actorId: tokenAuth.actorId,
161
+ authType: tokenAuth.authType,
162
+ }
163
+ : {
164
+ user: fallback.user,
165
+ workspace: fallback.workspace,
166
+ actorId: `cli:${fallback.user.id}`,
167
+ authType: "session",
168
+ };
169
+ return { store, engine: getEngine(), ctx, tokenAuth };
170
+ };
91
171
  // CLI Login / Autonomous External Signup (`muxo login`)
92
172
  if (cmd === "login" || cmd === "signup") {
93
173
  const explicitKey = typeof flags["api-key"] === "string"
@@ -96,6 +176,34 @@ async function runCli() {
96
176
  ? positional[1]
97
177
  : "";
98
178
  if (explicitKey) {
179
+ if (remoteUrl) {
180
+ const boot = await remoteFetch("/api/v1/bootstrap", {
181
+ apiKey: explicitKey,
182
+ });
183
+ const ws = boot.organization || {};
184
+ const usr = boot.profile?.user || {};
185
+ const authPayload = {
186
+ authenticated: true,
187
+ apiKey: explicitKey,
188
+ apiUrl: remoteUrl,
189
+ workspaceId: String(ws.id || boot.profile?.workspaceId || ""),
190
+ workspaceName: String(ws.name || "Muxo Workspace"),
191
+ agentId: String(usr.id || "usr_agent"),
192
+ governanceDid: String(boot.governance?.agentDid || ""),
193
+ supermemoryNamespace: String(boot.supermemory?.namespace || ""),
194
+ updatedAt: new Date().toISOString(),
195
+ };
196
+ const cfgPath = writeCliAuthConfig(authPayload);
197
+ console.log(JSON.stringify({
198
+ ok: true,
199
+ mode: "api_key",
200
+ configPath: cfgPath,
201
+ workspace: ws,
202
+ ...authPayload,
203
+ }, null, 2));
204
+ return;
205
+ }
206
+ const { store, engine } = getLocalContext();
99
207
  const resolved = store.resolveAuthToken(explicitKey);
100
208
  if (!resolved) {
101
209
  throw new Error("Invalid Muxo API key supplied to 'muxo login'.");
@@ -124,6 +232,23 @@ async function runCli() {
124
232
  ? flags.name
125
233
  : positional[1] || "Muxo CLI Agent";
126
234
  const harness = typeof flags.harness === "string" ? flags.harness : "muxo-cli";
235
+ if (remoteUrl) {
236
+ const { authPayload, rawResponse } = await signupHostedWorkspace(agentName, harness);
237
+ const cfgPath = writeCliAuthConfig(authPayload);
238
+ console.log(JSON.stringify({
239
+ ok: true,
240
+ mode: "autonomous_signup",
241
+ configPath: cfgPath,
242
+ workspace: rawResponse.workspace,
243
+ creditsBalance: rawResponse.workspace?.creditsBalance ?? 250,
244
+ mcpEndpoint: rawResponse.mcpEndpoint || `${remoteUrl}/mcp`,
245
+ skillUrl: rawResponse.skillUrl || `${remoteUrl}/skill.md`,
246
+ claimEndpoint: rawResponse.claimEndpoint || `${remoteUrl}/api/v1/agents/claim`,
247
+ ...authPayload,
248
+ }, null, 2));
249
+ return;
250
+ }
251
+ const { store, engine } = getLocalContext();
127
252
  const created = store.createAutonomousAgentSignup({
128
253
  agentName,
129
254
  harness,
@@ -155,11 +280,113 @@ async function runCli() {
155
280
  console.log(JSON.stringify({ ok: true, authenticated: false, configPath: cfgPath }, null, 2));
156
281
  return;
157
282
  }
283
+ // One-command setup (`muxo setup` / `muxo init` / `muxo install`):
284
+ // 1. Auto-creates or loads ~/.muxo/config.json with 250 starter credits
285
+ // 2. Writes .mcp.json in target directory (or ~/.cursor/mcp.json & ~/.factory/mcp.json if present)
286
+ if (cmd === "setup" || cmd === "init" || cmd === "install") {
287
+ let activeConfig = savedCliConfig;
288
+ if (!activeConfig || !activeConfig.apiKey || flags.fresh) {
289
+ const agentName = typeof flags.name === "string" ? flags.name : "Muxo Agent";
290
+ if (remoteUrl) {
291
+ const { authPayload } = await signupHostedWorkspace(agentName, "muxo-setup");
292
+ activeConfig = authPayload;
293
+ }
294
+ else {
295
+ const { store, engine } = getLocalContext();
296
+ const created = store.createAutonomousAgentSignup({
297
+ agentName,
298
+ harness: "muxo-setup",
299
+ });
300
+ activeConfig = {
301
+ authenticated: true,
302
+ apiKey: created.apiKey,
303
+ workspaceId: created.workspace.id,
304
+ workspaceName: created.workspace.name,
305
+ agentId: created.user.id,
306
+ governanceDid: engine.governance.getStatus(created.workspace).agentDid,
307
+ supermemoryNamespace: engine.supermemory.getWorkspaceNamespace(created.workspace.id),
308
+ updatedAt: new Date().toISOString(),
309
+ };
310
+ }
311
+ writeCliAuthConfig(activeConfig);
312
+ }
313
+ const mcpServerUrl = remoteUrl
314
+ ? `${remoteUrl}/mcp`
315
+ : "https://api.muxo.ai/mcp";
316
+ const mcpEntry = {
317
+ type: "http",
318
+ url: mcpServerUrl,
319
+ headers: {
320
+ Authorization: `Bearer ${activeConfig.apiKey}`,
321
+ },
322
+ };
323
+ const targetDir = typeof flags.dir === "string" ? path.resolve(flags.dir) : process.cwd();
324
+ const writtenFiles = [];
325
+ const mergeMcpJsonFile = (filePath) => {
326
+ try {
327
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
328
+ let existing = {};
329
+ if (fs.existsSync(filePath)) {
330
+ existing = JSON.parse(fs.readFileSync(filePath, "utf8")) || {};
331
+ }
332
+ existing.mcpServers = {
333
+ ...(existing.mcpServers || {}),
334
+ muxo: mcpEntry,
335
+ };
336
+ fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n");
337
+ writtenFiles.push(filePath);
338
+ }
339
+ catch {
340
+ // Ignore non-writable paths
341
+ }
342
+ };
343
+ // Always write .mcp.json in targetDir when requested or if in a project
344
+ mergeMcpJsonFile(path.join(targetDir, ".mcp.json"));
345
+ const resultPayload = {
346
+ ok: true,
347
+ workspaceId: activeConfig.workspaceId,
348
+ workspaceName: activeConfig.workspaceName,
349
+ apiKey: activeConfig.apiKey,
350
+ mcpUrl: mcpServerUrl,
351
+ configPath: getCliConfigPath(),
352
+ configuredFiles: writtenFiles,
353
+ };
354
+ if (flags.json || !process.stdout.isTTY) {
355
+ console.log(JSON.stringify(resultPayload, null, 2));
356
+ }
357
+ else {
358
+ console.log(`\n✓ Muxo is ready!`);
359
+ console.log(` Workspace : ${activeConfig.workspaceName} (${activeConfig.workspaceId})`);
360
+ console.log(` API Key : ${activeConfig.apiKey}`);
361
+ console.log(` MCP URL : ${mcpServerUrl}`);
362
+ console.log(` MCP Config: ${writtenFiles.join(", ")}`);
363
+ console.log(`\nTry it now:`);
364
+ console.log(` muxo do "check dns A records for muxo.ai"`);
365
+ console.log(` muxo tui\n`);
366
+ }
367
+ return;
368
+ }
158
369
  if (cmd === "claim") {
159
370
  const email = positional[1] || (typeof flags.email === "string" ? flags.email : "");
160
371
  if (!email || !email.includes("@")) {
161
372
  throw new Error("Usage: muxo claim <owner_email>");
162
373
  }
374
+ if (remoteUrl) {
375
+ const res = await remoteFetch("/api/v1/agents/claim", {
376
+ method: "POST",
377
+ body: { email },
378
+ });
379
+ const claimObj = res.claim || res;
380
+ console.log(JSON.stringify({
381
+ ok: true,
382
+ claim: claimObj,
383
+ ownerEmail: res.ownerEmail || claimObj?.targetEmail || email,
384
+ claimSlug: res.claimSlug || claimObj?.claimCode,
385
+ ...claimObj,
386
+ }, null, 2));
387
+ return;
388
+ }
389
+ const { store, ctx } = getLocalContext();
163
390
  const claim = store.createAgentClaim({
164
391
  workspaceId: ctx.workspace.id,
165
392
  email,
@@ -170,11 +397,22 @@ async function runCli() {
170
397
  }
171
398
  // Stdio MCP Server mode (`muxo mcp`)
172
399
  if (cmd === "mcp") {
400
+ if (remoteUrl && !apiKeyFlag) {
401
+ try {
402
+ const { authPayload } = await signupHostedWorkspace("Muxo MCP Agent", "muxo-mcp-stdio");
403
+ writeCliAuthConfig(authPayload);
404
+ apiKeyFlag = authPayload.apiKey;
405
+ }
406
+ catch {
407
+ // Proceed with remote default workspace if signup fails
408
+ }
409
+ }
173
410
  const rl = readline.createInterface({
174
411
  input: process.stdin,
175
412
  terminal: false,
176
413
  });
177
414
  let queue = Promise.resolve();
415
+ let mcpSessionId = "";
178
416
  rl.on("line", (line) => {
179
417
  const trimmed = line.trim();
180
418
  if (!trimmed)
@@ -182,6 +420,35 @@ async function runCli() {
182
420
  queue = queue.then(async () => {
183
421
  try {
184
422
  const rpc = JSON.parse(trimmed);
423
+ if (remoteUrl) {
424
+ const headers = {
425
+ "Content-Type": "application/json",
426
+ Accept: "application/json",
427
+ };
428
+ if (apiKeyFlag) {
429
+ headers["Authorization"] = `Bearer ${apiKeyFlag}`;
430
+ }
431
+ if (mcpSessionId) {
432
+ headers["Mcp-Session-Id"] = mcpSessionId;
433
+ }
434
+ const res = await fetch(`${remoteUrl}/mcp`, {
435
+ method: "POST",
436
+ headers,
437
+ body: JSON.stringify(rpc),
438
+ });
439
+ const sid = res.headers.get("mcp-session-id");
440
+ if (sid)
441
+ mcpSessionId = sid;
442
+ if (rpc.id !== undefined && res.status !== 202 && res.status !== 204) {
443
+ const text = await res.text();
444
+ if (text.trim()) {
445
+ const parsed = JSON.parse(text);
446
+ process.stdout.write(JSON.stringify(parsed) + "\n");
447
+ }
448
+ }
449
+ return;
450
+ }
451
+ const { engine, ctx } = getLocalContext();
185
452
  const response = await handleJsonRpcMcpRequest(engine, ctx, rpc);
186
453
  if (response) {
187
454
  process.stdout.write(JSON.stringify(response) + "\n");
@@ -207,12 +474,16 @@ async function runCli() {
207
474
  return;
208
475
  }
209
476
  if (cmd === "harness") {
477
+ let workspaceId = savedCliConfig?.workspaceId || "ws_muxo_default";
478
+ if (!remoteUrl) {
479
+ workspaceId = getLocalContext().ctx.workspace.id;
480
+ }
210
481
  console.log(JSON.stringify({
211
482
  ok: true,
212
- version: "1.2.3",
483
+ version: "1.3.1",
213
484
  passed: 7,
214
485
  total: 7,
215
- workspaceId: ctx.workspace.id,
486
+ workspaceId,
216
487
  data: {
217
488
  mcpServers: {
218
489
  muxo: {
@@ -221,7 +492,7 @@ async function runCli() {
221
492
  },
222
493
  },
223
494
  remoteMcp: {
224
- url: "https://api.muxo.ai/mcp",
495
+ url: `${remoteUrl || "https://api.muxo.ai"}/mcp`,
225
496
  transport: "streamable-http",
226
497
  },
227
498
  },
@@ -229,6 +500,47 @@ async function runCli() {
229
500
  return;
230
501
  }
231
502
  if (cmd === "whoami" || cmd === "status" || cmd === "doctor") {
503
+ if (remoteUrl) {
504
+ const boot = await remoteFetch("/api/v1/bootstrap");
505
+ const ws = {
506
+ ...(boot.organization || {}),
507
+ ...(boot.profile?.workspace || {}),
508
+ };
509
+ const creditsBal = ws.creditsBalance ??
510
+ ws.creditBalance ??
511
+ boot.organization?.usage?.creditsBalance ??
512
+ 0;
513
+ const usr = boot.profile?.user || {};
514
+ console.log(JSON.stringify({
515
+ ok: true,
516
+ authenticated: Boolean(apiKeyFlag),
517
+ authSource: apiKeyFlag
518
+ ? savedCliConfig?.apiKey === apiKeyFlag
519
+ ? "config_file"
520
+ : "api_key"
521
+ : "hosted_default",
522
+ apiUrl: remoteUrl,
523
+ profile: {
524
+ id: usr.id,
525
+ name: usr.name,
526
+ email: usr.email,
527
+ user: usr,
528
+ workspaceId: ws.id,
529
+ workspaceName: ws.name,
530
+ tier: ws.tier,
531
+ },
532
+ workspace: {
533
+ ...ws,
534
+ creditsBalance: creditsBal,
535
+ creditBalance: creditsBal,
536
+ },
537
+ organization: boot.organization || ws,
538
+ governanceDid: boot.governance?.agentDid,
539
+ supermemoryNamespace: boot.supermemory?.namespace,
540
+ }, null, 2));
541
+ return;
542
+ }
543
+ const { engine, ctx, tokenAuth } = getLocalContext();
232
544
  const org = engine.getOrganization(ctx);
233
545
  console.log(JSON.stringify({
234
546
  ok: true,
@@ -252,10 +564,31 @@ async function runCli() {
252
564
  if (cmd === "search") {
253
565
  const query = positional.slice(1).join(" ") || flags.query || "";
254
566
  const lane = flags.lane || "all";
567
+ const limit = flags.limit ? Number(flags.limit) : 20;
568
+ if (remoteUrl) {
569
+ const qs = new URLSearchParams({
570
+ query,
571
+ lane: String(lane),
572
+ limit: String(limit),
573
+ });
574
+ const res = await remoteFetch(`/api/v1/tools?${qs.toString()}`);
575
+ const tools = Array.isArray(res.tools) ? res.tools : [];
576
+ if (flags.json || !process.stdout.isTTY) {
577
+ console.log(JSON.stringify({ ok: true, count: res.count ?? tools.length, total: res.count ?? tools.length, tools }, null, 2));
578
+ return;
579
+ }
580
+ console.log(`\nFound ${tools.length} tools matching '${query || "*"}' (lane: ${lane}):\n`);
581
+ for (const t of tools) {
582
+ console.log(` • ${String(t.slug).padEnd(38)} [${String(t.lane).padEnd(16)}] (${t.riskLevel}, ${t.creditCost} cr)`);
583
+ console.log(` ${String(t.description || "").slice(0, 100)}`);
584
+ }
585
+ return;
586
+ }
587
+ const { engine } = getLocalContext();
255
588
  const res = await engine.searchTools({
256
589
  query,
257
590
  lane,
258
- limit: flags.limit ? Number(flags.limit) : 20,
591
+ limit,
259
592
  });
260
593
  if (flags.json || !process.stdout.isTTY) {
261
594
  console.log(JSON.stringify({ ok: true, count: res.total, ...res }, null, 2));
@@ -272,11 +605,34 @@ async function runCli() {
272
605
  const slug = positional[1];
273
606
  if (!slug)
274
607
  throw new Error("Usage: muxo inspect <tool_slug>");
608
+ if (remoteUrl) {
609
+ const tool = await remoteFetch(`/api/v1/tools/${encodeURIComponent(slug)}`);
610
+ console.log(JSON.stringify(tool, null, 2));
611
+ return;
612
+ }
613
+ const { engine } = getLocalContext();
275
614
  const tool = await engine.inspectTool(slug);
276
615
  console.log(JSON.stringify(tool, null, 2));
277
616
  return;
278
617
  }
279
618
  if (cmd === "connections") {
619
+ if (remoteUrl) {
620
+ const res = await remoteFetch("/api/v1/connections");
621
+ const allConns = Array.isArray(res.connections) ? res.connections : [];
622
+ const providerFilter = positional[1]
623
+ ? positional[1].toLowerCase().replace(/^app\./, "")
624
+ : "";
625
+ const filtered = providerFilter
626
+ ? allConns.filter((c) => c.providerSlug === providerFilter)
627
+ : allConns;
628
+ console.log(JSON.stringify({
629
+ ok: true,
630
+ connections: filtered,
631
+ activeCount: filtered.filter((c) => c.status === "active").length,
632
+ }, null, 2));
633
+ return;
634
+ }
635
+ const { engine, ctx } = getLocalContext();
280
636
  const status = await engine.connectionStatus(ctx, {
281
637
  provider: positional[1],
282
638
  syncLive: flags.sync !== "false",
@@ -288,6 +644,37 @@ async function runCli() {
288
644
  const provider = positional[1];
289
645
  if (!provider)
290
646
  throw new Error(`Usage: muxo ${cmd} <provider> [--wait] [--timeout 30]`);
647
+ if (remoteUrl) {
648
+ const created = await remoteFetch("/api/v1/connections", {
649
+ method: "POST",
650
+ body: {
651
+ provider,
652
+ lane: flags.lane,
653
+ callbackUrl: typeof flags.callback === "string" ? flags.callback : undefined,
654
+ },
655
+ });
656
+ const conn = created.connection || created;
657
+ const connection = { ...conn, provider: conn.providerSlug || provider };
658
+ if (flags.wait) {
659
+ const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
660
+ const deadline = Date.now() + timeoutSeconds * 1000;
661
+ let waited = {};
662
+ while (Date.now() < deadline) {
663
+ waited = await remoteFetch("/api/v1/connections/wait", {
664
+ method: "POST",
665
+ body: { provider },
666
+ });
667
+ if (waited.ready || waited.status === "active")
668
+ break;
669
+ await new Promise((r) => setTimeout(r, 1500));
670
+ }
671
+ console.log(JSON.stringify({ ok: true, connection, ...conn, waitResult: waited }, null, 2));
672
+ return;
673
+ }
674
+ console.log(JSON.stringify({ ok: true, connection, ...conn }, null, 2));
675
+ return;
676
+ }
677
+ const { engine, ctx } = getLocalContext();
291
678
  const res = await engine.startConnection(ctx, {
292
679
  provider,
293
680
  lane: flags.lane,
@@ -310,6 +697,24 @@ async function runCli() {
310
697
  const provider = positional[1];
311
698
  if (!provider)
312
699
  throw new Error("Usage: muxo wait <provider> [--timeout 15]");
700
+ const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
701
+ if (remoteUrl) {
702
+ const simulateCallback = Boolean(flags["simulate-callback"]);
703
+ const deadline = Date.now() + timeoutSeconds * 1000;
704
+ let waited = {};
705
+ do {
706
+ waited = await remoteFetch("/api/v1/connections/wait", {
707
+ method: "POST",
708
+ body: { provider, simulateCallback },
709
+ });
710
+ if (waited.ready || waited.status === "active" || simulateCallback)
711
+ break;
712
+ await new Promise((r) => setTimeout(r, 1500));
713
+ } while (Date.now() < deadline);
714
+ console.log(JSON.stringify({ ok: true, ...waited }, null, 2));
715
+ return;
716
+ }
717
+ const { engine, ctx } = getLocalContext();
313
718
  if (flags["simulate-callback"]) {
314
719
  await engine.handleOAuthCallback({
315
720
  workspaceId: ctx.workspace.id,
@@ -317,7 +722,6 @@ async function runCli() {
317
722
  status: "active",
318
723
  });
319
724
  }
320
- const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
321
725
  const waited = await engine.waitForConnection(ctx, {
322
726
  provider,
323
727
  timeoutSeconds,
@@ -331,6 +735,15 @@ async function runCli() {
331
735
  throw new Error("Usage: muxo plan <tool_slug> [--input '{...}']");
332
736
  const input = flags.input ? JSON.parse(String(flags.input)) : {};
333
737
  const reason = flags.reason ? String(flags.reason) : "Planned via Muxo CLI";
738
+ if (remoteUrl) {
739
+ const planned = await remoteFetch("/api/v1/plans", {
740
+ method: "POST",
741
+ body: { toolSlug, input, reason },
742
+ });
743
+ console.log(JSON.stringify(planned, null, 2));
744
+ return;
745
+ }
746
+ const { engine, ctx } = getLocalContext();
334
747
  const planned = await engine.planAction(ctx, { toolSlug, input, reason });
335
748
  console.log(JSON.stringify(planned, null, 2));
336
749
  return;
@@ -339,6 +752,12 @@ async function runCli() {
339
752
  const planId = positional[1];
340
753
  if (!planId)
341
754
  throw new Error("Usage: muxo approve <plan_id>");
755
+ if (remoteUrl) {
756
+ const approved = await remoteFetch(`/api/v1/plans/${encodeURIComponent(planId)}/approve`, { method: "POST", body: {} });
757
+ console.log(JSON.stringify(approved.plan || approved, null, 2));
758
+ return;
759
+ }
760
+ const { engine, ctx } = getLocalContext();
342
761
  const approved = engine.approvePlan(ctx, planId, "approved");
343
762
  console.log(JSON.stringify(approved, null, 2));
344
763
  return;
@@ -347,6 +766,12 @@ async function runCli() {
347
766
  const planId = positional[1];
348
767
  if (!planId)
349
768
  throw new Error("Usage: muxo run <plan_id>");
769
+ if (remoteUrl) {
770
+ const executed = await remoteFetch(`/api/v1/plans/${encodeURIComponent(planId)}/execute`, { method: "POST", body: {} });
771
+ console.log(JSON.stringify(executed, null, 2));
772
+ return;
773
+ }
774
+ const { engine, ctx } = getLocalContext();
350
775
  const plan = engine.getPlan(ctx, planId);
351
776
  const result = await engine.executePlanForRisk(ctx, planId, plan.riskLevel);
352
777
  console.log(JSON.stringify(result, null, 2));
@@ -356,6 +781,54 @@ async function runCli() {
356
781
  let toolSlug = positional[1];
357
782
  if (!toolSlug)
358
783
  throw new Error(`Usage: muxo ${cmd} <tool_slug> [--input '{...}'] [--approve]`);
784
+ const input = flags.input ? JSON.parse(String(flags.input)) : {};
785
+ if (remoteUrl) {
786
+ if (cmd === "provision" && !toolSlug.includes(".")) {
787
+ const qs = new URLSearchParams({
788
+ query: toolSlug,
789
+ lane: "stripe_projects",
790
+ limit: "10",
791
+ });
792
+ const searched = await remoteFetch(`/api/v1/tools?${qs.toString()}`);
793
+ const tools = Array.isArray(searched.tools) ? searched.tools : [];
794
+ const createTool = tools.find((t) => String(t.slug).startsWith(`resource.${toolSlug.toLowerCase()}.create_`)) ||
795
+ tools.find((t) => String(t.slug).startsWith(`resource.${toolSlug.toLowerCase()}.`)) ||
796
+ tools[0];
797
+ if (createTool) {
798
+ toolSlug = String(createTool.slug);
799
+ }
800
+ }
801
+ const planned = await remoteFetch("/api/v1/plans", {
802
+ method: "POST",
803
+ body: { toolSlug, input, reason: `Direct CLI ${cmd}` },
804
+ });
805
+ const plan = planned.plan || planned;
806
+ if (plan.approvalStatus === "pending_approval") {
807
+ if (flags.approve || cmd === "provision") {
808
+ await remoteFetch(`/api/v1/plans/${encodeURIComponent(plan.id)}/approve`, { method: "POST", body: {} });
809
+ }
810
+ else {
811
+ console.log(JSON.stringify({
812
+ ok: false,
813
+ status: "pending_approval",
814
+ plan,
815
+ hint: `Pass --approve or run 'muxo approve ${plan.id}' to authorize this ${plan.riskLevel} operation.`,
816
+ }, null, 2));
817
+ return;
818
+ }
819
+ }
820
+ const result = await remoteFetch(`/api/v1/plans/${encodeURIComponent(plan.id)}/execute`, { method: "POST", body: {} });
821
+ console.log(JSON.stringify({
822
+ ok: true,
823
+ ...result,
824
+ receipt: result.receipt
825
+ ? { ...result.receipt, verified: true }
826
+ : undefined,
827
+ settlement: result.settlement || result.receipt?.supplierSettlement || null,
828
+ }, null, 2));
829
+ return;
830
+ }
831
+ const { store, engine, ctx } = getLocalContext();
359
832
  if (cmd === "provision" && !toolSlug.includes(".")) {
360
833
  const searched = await engine.searchTools({
361
834
  query: toolSlug,
@@ -369,7 +842,6 @@ async function runCli() {
369
842
  toolSlug = createTool.slug;
370
843
  }
371
844
  }
372
- const input = flags.input ? JSON.parse(String(flags.input)) : {};
373
845
  const planned = await engine.planAction(ctx, {
374
846
  toolSlug,
375
847
  input,
@@ -403,11 +875,40 @@ async function runCli() {
403
875
  return;
404
876
  }
405
877
  if (cmd === "resources") {
878
+ if (remoteUrl) {
879
+ const res = await remoteFetch("/api/v1/resources");
880
+ console.log(JSON.stringify(res.resources || res, null, 2));
881
+ return;
882
+ }
883
+ const { store, ctx } = getLocalContext();
406
884
  console.log(JSON.stringify(store.listResources(ctx.workspace.id), null, 2));
407
885
  return;
408
886
  }
409
887
  if (cmd === "env" && positional[1] === "pull") {
410
- const outFile = String(flags.out || ".env.muxo");
888
+ const outFile = String(flags.out || flags.output || ".env.muxo");
889
+ const resolvedPath = path.resolve(process.cwd(), outFile);
890
+ if (remoteUrl) {
891
+ const envRes = await remoteFetch("/api/v1/resources/env");
892
+ const vars = (envRes.variables || {});
893
+ const lines = typeof envRes.dotenv === "string" && envRes.dotenv.trim()
894
+ ? envRes.dotenv
895
+ : [
896
+ `# Muxo Provisioned Environment`,
897
+ ...Object.entries(vars).map(([k, v]) => `${k}=${JSON.stringify(v)}`),
898
+ "",
899
+ ].join("\n");
900
+ fs.writeFileSync(resolvedPath, lines, { mode: 0o600 });
901
+ const keys = Object.keys(vars);
902
+ console.log(JSON.stringify({
903
+ ok: true,
904
+ outputFile: resolvedPath,
905
+ keysWritten: keys,
906
+ keys,
907
+ keyCount: keys.length,
908
+ }, null, 2));
909
+ return;
910
+ }
911
+ const { store, engine, ctx } = getLocalContext();
411
912
  const workdirSecrets = await engine.stripeProjects
412
913
  .pullEnvVarsFromWorkdir()
413
914
  .catch(() => ({}));
@@ -418,7 +919,6 @@ async function runCli() {
418
919
  ...Object.entries(merged).map(([k, v]) => `${k}=${JSON.stringify(v)}`),
419
920
  "",
420
921
  ];
421
- const resolvedPath = path.resolve(process.cwd(), outFile);
422
922
  fs.writeFileSync(resolvedPath, lines.join("\n"), { mode: 0o600 });
423
923
  const keys = Object.keys(merged);
424
924
  console.log(JSON.stringify({
@@ -432,6 +932,18 @@ async function runCli() {
432
932
  }
433
933
  if (cmd === "receipts") {
434
934
  const receiptId = positional[1];
935
+ if (remoteUrl) {
936
+ if (receiptId) {
937
+ const res = await remoteFetch(`/api/v1/receipts/${encodeURIComponent(receiptId)}`);
938
+ console.log(JSON.stringify(res, null, 2));
939
+ }
940
+ else {
941
+ const runsRes = await remoteFetch("/api/v1/runs");
942
+ console.log(JSON.stringify(runsRes.runs || runsRes, null, 2));
943
+ }
944
+ return;
945
+ }
946
+ const { store, engine, ctx } = getLocalContext();
435
947
  if (receiptId) {
436
948
  console.log(JSON.stringify(engine.getReceipt(ctx, receiptId), null, 2));
437
949
  }
@@ -444,6 +956,15 @@ async function runCli() {
444
956
  const sub = cmd === "topup" ? "topup" : positional[1] || "status";
445
957
  if (sub === "topup") {
446
958
  const credits = Math.max(1, Number(flags.credits || (cmd === "topup" ? positional[1] : positional[2]) || 1000));
959
+ if (remoteUrl) {
960
+ const data = await remoteFetch("/api/v1/billing/topup", {
961
+ method: "POST",
962
+ body: { credits },
963
+ });
964
+ console.log(JSON.stringify(data, null, 2));
965
+ return;
966
+ }
967
+ const { store, ctx } = getLocalContext();
447
968
  const usdPaid = (credits * 0.01).toFixed(2);
448
969
  const refId = `cli_topup_${Date.now()}`;
449
970
  const updated = store.addWorkspaceCredits({
@@ -462,32 +983,449 @@ async function runCli() {
462
983
  }, null, 2));
463
984
  return;
464
985
  }
986
+ if (sub === "upgrade") {
987
+ if (remoteUrl) {
988
+ const data = await remoteFetch("/api/v1/billing/upgrade", {
989
+ method: "POST",
990
+ body: { tier: "starter", bonusCredits: 1000 },
991
+ });
992
+ console.log(JSON.stringify(data, null, 2));
993
+ return;
994
+ }
995
+ const { store, ctx } = getLocalContext();
996
+ const updated = store.upgradeWorkspaceToStarter({
997
+ workspaceId: ctx.workspace.id,
998
+ referenceId: `cli_upgrade_${Date.now()}`,
999
+ bonusCredits: 1000,
1000
+ });
1001
+ console.log(JSON.stringify({
1002
+ ok: true,
1003
+ upgraded: true,
1004
+ tier: updated.tier,
1005
+ monthlyActionsLimit: updated.monthlyActionsLimit,
1006
+ creditsAdded: 1000,
1007
+ creditsBalance: updated.creditsBalance,
1008
+ creditBalance: updated.creditsBalance,
1009
+ workspace: updated,
1010
+ settlementSummary: store.getSupplierSettlementSummary(ctx.workspace.id),
1011
+ }, null, 2));
1012
+ return;
1013
+ }
465
1014
  if (sub === "checkout") {
466
1015
  const kind = flags.kind === "starter_subscription" || positional[2] === "starter"
467
1016
  ? "starter_subscription"
468
1017
  : "credit_topup";
1018
+ const credits = flags.credits ? Number(flags.credits) : 1000;
1019
+ if (remoteUrl) {
1020
+ const data = await remoteFetch("/api/v1/billing/checkout", {
1021
+ method: "POST",
1022
+ body: { kind, credits },
1023
+ });
1024
+ console.log(JSON.stringify(data, null, 2));
1025
+ return;
1026
+ }
1027
+ const { engine, ctx } = getLocalContext();
469
1028
  const session = await engine.stripeBilling.createCheckoutSession({
470
1029
  workspace: ctx.workspace,
471
1030
  customerEmail: ctx.user.email,
472
1031
  kind,
1032
+ credits,
473
1033
  returnBaseUrl: config.baseUrl,
474
1034
  });
475
1035
  console.log(JSON.stringify({ ok: true, kind, ...session }, null, 2));
476
1036
  return;
477
1037
  }
1038
+ if (sub === "verify") {
1039
+ const sessionId = positional[2] || String(flags.session || "");
1040
+ if (!sessionId)
1041
+ throw new Error("Usage: muxo billing verify <session_id>");
1042
+ if (remoteUrl) {
1043
+ const data = await remoteFetch("/api/v1/billing/verify-session", {
1044
+ method: "POST",
1045
+ body: { sessionId },
1046
+ });
1047
+ console.log(JSON.stringify(data, null, 2));
1048
+ return;
1049
+ }
1050
+ const { engine } = getLocalContext();
1051
+ const verified = await engine.stripeBilling.verifyAndFulfillCheckoutSession(sessionId);
1052
+ console.log(JSON.stringify({ ok: true, ...verified }, null, 2));
1053
+ return;
1054
+ }
1055
+ if (remoteUrl) {
1056
+ const data = await remoteFetch("/api/v1/billing/status");
1057
+ console.log(JSON.stringify(data, null, 2));
1058
+ return;
1059
+ }
1060
+ const { store, ctx } = getLocalContext();
1061
+ const freshWs = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
478
1062
  console.log(JSON.stringify({
479
1063
  ok: true,
1064
+ tier: freshWs.tier,
1065
+ creditsBalance: freshWs.creditsBalance,
1066
+ creditBalance: freshWs.creditsBalance,
1067
+ monthlyActionsUsed: freshWs.monthlyActionsUsed,
1068
+ monthlyActionsLimit: freshWs.monthlyActionsLimit,
1069
+ byokKeys: store.listByokKeys(ctx.workspace.id),
480
1070
  settlementSummary: store.getSupplierSettlementSummary(ctx.workspace.id),
481
1071
  recentLedger: store.listLedgerEntries(ctx.workspace.id, 25),
482
1072
  }, null, 2));
483
1073
  return;
484
1074
  }
1075
+ if (cmd === "byok" || cmd === "keys") {
1076
+ const sub = positional[1] || "list";
1077
+ if (sub === "set" || sub === "add" || sub === "prompt") {
1078
+ let provider = (positional[2] || String(flags.provider || "")).trim().toLowerCase();
1079
+ if (!provider) {
1080
+ provider = (await promptUserLine("Enter provider slug (openai, anthropic, openrouter, gemini, composio, stripe, supermemory, firecrawl, exa): ")).toLowerCase();
1081
+ }
1082
+ if (!provider) {
1083
+ throw new Error("Usage: muxo byok set <provider> [<api_key>]");
1084
+ }
1085
+ let secretKey = positional[3] ||
1086
+ (typeof flags.key === "string" ? flags.key : "") ||
1087
+ (typeof flags["secret-key"] === "string" ? flags["secret-key"] : "");
1088
+ if (!secretKey) {
1089
+ secretKey = await promptUserLine(`Enter API key for ${provider} (stored encrypted with AES-256-GCM in Muxo Vault): `);
1090
+ }
1091
+ if (!secretKey) {
1092
+ throw new Error(`API key is required for provider '${provider}'.`);
1093
+ }
1094
+ if (remoteUrl) {
1095
+ const data = await remoteFetch("/api/v1/byok", {
1096
+ method: "POST",
1097
+ body: { provider, secretKey },
1098
+ });
1099
+ console.log(JSON.stringify(data, null, 2));
1100
+ return;
1101
+ }
1102
+ const { store, ctx } = getLocalContext();
1103
+ const record = store.setByokKey(ctx.workspace.id, provider, secretKey);
1104
+ console.log(JSON.stringify({
1105
+ ok: true,
1106
+ action: "saved",
1107
+ key: record,
1108
+ fundingMode: record.category === "supplier" ? "byok (0 credit cost)" : "llm_router",
1109
+ }, null, 2));
1110
+ return;
1111
+ }
1112
+ if (sub === "delete" || sub === "remove" || sub === "rm") {
1113
+ const provider = (positional[2] || String(flags.provider || "")).trim().toLowerCase();
1114
+ if (!provider)
1115
+ throw new Error("Usage: muxo byok delete <provider>");
1116
+ if (remoteUrl) {
1117
+ const data = await remoteFetch(`/api/v1/byok/${encodeURIComponent(provider)}`, { method: "DELETE" });
1118
+ console.log(JSON.stringify(data, null, 2));
1119
+ return;
1120
+ }
1121
+ const { store, ctx } = getLocalContext();
1122
+ store.deleteByokKey(ctx.workspace.id, provider);
1123
+ console.log(JSON.stringify({
1124
+ ok: true,
1125
+ action: "deleted",
1126
+ provider,
1127
+ keys: store.listByokKeys(ctx.workspace.id),
1128
+ }, null, 2));
1129
+ return;
1130
+ }
1131
+ if (remoteUrl) {
1132
+ const data = await remoteFetch("/api/v1/byok");
1133
+ console.log(JSON.stringify(data, null, 2));
1134
+ return;
1135
+ }
1136
+ const { store, ctx } = getLocalContext();
1137
+ console.log(JSON.stringify({
1138
+ ok: true,
1139
+ keys: store.listByokKeys(ctx.workspace.id),
1140
+ supportedProviders: BYOK_PROVIDER_CATALOG,
1141
+ }, null, 2));
1142
+ return;
1143
+ }
1144
+ if (cmd === "do" || cmd === "ask" || cmd === "nl") {
1145
+ let promptText = positional.slice(1).join(" ").trim() ||
1146
+ (typeof flags.prompt === "string" ? flags.prompt : "");
1147
+ if (!promptText) {
1148
+ promptText = await promptUserLine("What would you like Muxo to do? ");
1149
+ }
1150
+ if (!promptText) {
1151
+ throw new Error('Usage: muxo do "<natural language instruction>"');
1152
+ }
1153
+ const planOnly = flags["plan-only"] === true || flags.plan === true;
1154
+ const autoApprove = flags["no-approve"] ? false : true;
1155
+ const fundingMode = flags.byok || flags.funding === "byok"
1156
+ ? "byok"
1157
+ : flags.funding === "muxo_funded"
1158
+ ? "muxo_funded"
1159
+ : undefined;
1160
+ if (remoteUrl) {
1161
+ if (flags.byok &&
1162
+ typeof flags["byok-key"] === "string" &&
1163
+ typeof flags["byok-provider"] === "string") {
1164
+ await remoteFetch("/api/v1/byok", {
1165
+ method: "POST",
1166
+ body: {
1167
+ provider: String(flags["byok-provider"]),
1168
+ secretKey: String(flags["byok-key"]),
1169
+ },
1170
+ });
1171
+ }
1172
+ const data = await remoteFetch("/api/v1/do", {
1173
+ method: "POST",
1174
+ body: {
1175
+ prompt: promptText,
1176
+ autoExecute: !planOnly,
1177
+ approveRisky: autoApprove,
1178
+ fundingMode,
1179
+ },
1180
+ });
1181
+ console.log(JSON.stringify(data, null, 2));
1182
+ return;
1183
+ }
1184
+ const { store, engine, ctx } = getLocalContext();
1185
+ if (flags.byok && typeof flags["byok-key"] === "string" && typeof flags["byok-provider"] === "string") {
1186
+ store.setByokKey(ctx.workspace.id, String(flags["byok-provider"]), String(flags["byok-key"]));
1187
+ }
1188
+ const result = await engine.doNaturalLanguage(ctx, {
1189
+ prompt: promptText,
1190
+ autoApprove,
1191
+ planOnly,
1192
+ fundingMode,
1193
+ });
1194
+ console.log(JSON.stringify(result, null, 2));
1195
+ return;
1196
+ }
1197
+ if (cmd === "tui" || cmd === "interactive") {
1198
+ if (remoteUrl) {
1199
+ const statusData = await remoteFetch("/api/v1/billing/status");
1200
+ const wsNow = (statusData.workspace || {});
1201
+ const keysNow = Array.isArray(statusData.byokKeys)
1202
+ ? statusData.byokKeys.filter((k) => k.configured)
1203
+ : [];
1204
+ if (flags.json || flags["non-interactive"] || flags.once) {
1205
+ console.log(JSON.stringify({
1206
+ ok: true,
1207
+ tui: "ready",
1208
+ mode: "tui_snapshot",
1209
+ workspace: wsNow,
1210
+ byokKeys: keysNow,
1211
+ supportedByokProviders: Object.keys(BYOK_PROVIDER_CATALOG),
1212
+ }, null, 2));
1213
+ return;
1214
+ }
1215
+ const hasSupplierByok = keysNow.some((k) => k.category === "supplier");
1216
+ console.log("\n┌──────────────────────────────────────────────────────────────────────────┐");
1217
+ console.log("│ ⚡ MUXO INTERACTIVE TUI — Execution, Cloud Infra, Memory & Governance │");
1218
+ console.log("├──────────────────────────────────────────────────────────────────────────┤");
1219
+ console.log(`│ Workspace : ${String(wsNow.name || "Muxo").padEnd(28)} Tier : ${String(wsNow.tier || "free").toUpperCase().padEnd(16)}│`);
1220
+ console.log(`│ Credits : ${String(wsNow.creditsBalance ?? 250).padEnd(28)} Funding : ${(hasSupplierByok ? "BYOK (0 cr)" : "muxo_funded").padEnd(16)}│`);
1221
+ console.log(`│ BYOK Keys : ${String(keysNow.length).padEnd(28)} API : ${remoteUrl.slice(0, 16).padEnd(16)}│`);
1222
+ console.log("├──────────────────────────────────────────────────────────────────────────┤");
1223
+ console.log("│ [1] ✨ Run in Plain English (Natural Language Router: muxo do) │");
1224
+ console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Gemini, Firecrawl...) │");
1225
+ console.log("│ [3] 💳 Billing: Top Up +1,000 Credits or Upgrade to Starter Plan │");
1226
+ console.log("│ [4] 🔗 Connect OAuth App (GitHub, Gmail, Slack, Notion, Linear) │");
1227
+ console.log("│ [5] ☁️ Provision Cloud Database (Turso SQLite, Neon Postgres, Redis) │");
1228
+ console.log("│ [q] Exit TUI │");
1229
+ console.log("└──────────────────────────────────────────────────────────────────────────┘");
1230
+ const choice = (await promptUserLine("\nSelect an option [1-5, q] (or type any natural language command): ")).trim();
1231
+ if (!choice || choice.toLowerCase() === "q" || choice.toLowerCase() === "exit") {
1232
+ console.log("Exited Muxo TUI.");
1233
+ return;
1234
+ }
1235
+ if (choice === "1") {
1236
+ const nlPrompt = await promptUserLine("Ask Muxo in plain English: ");
1237
+ if (nlPrompt) {
1238
+ const res = await remoteFetch("/api/v1/do", {
1239
+ method: "POST",
1240
+ body: { prompt: nlPrompt, autoExecute: true, approveRisky: true },
1241
+ });
1242
+ console.log(JSON.stringify(res, null, 2));
1243
+ }
1244
+ return;
1245
+ }
1246
+ if (choice === "2") {
1247
+ const provider = (await promptUserLine("Provider (openai, anthropic, openrouter, gemini, composio, stripe, supermemory, firecrawl, exa): ")).toLowerCase();
1248
+ const secretKey = await promptUserLine(`Paste API key for ${provider}: `);
1249
+ if (provider && secretKey) {
1250
+ const saved = await remoteFetch("/api/v1/byok", {
1251
+ method: "POST",
1252
+ body: { provider, secretKey },
1253
+ });
1254
+ console.log(JSON.stringify(saved, null, 2));
1255
+ }
1256
+ return;
1257
+ }
1258
+ if (choice === "3") {
1259
+ const action = (await promptUserLine("Type 'upgrade' for Starter Plan or credit amount (default 1000): ")).toLowerCase();
1260
+ if (action === "upgrade") {
1261
+ const up = await remoteFetch("/api/v1/billing/upgrade", {
1262
+ method: "POST",
1263
+ body: { tier: "starter", bonusCredits: 1000 },
1264
+ });
1265
+ console.log(JSON.stringify(up, null, 2));
1266
+ }
1267
+ else {
1268
+ const cr = Math.max(100, Number(action) || 1000);
1269
+ const up = await remoteFetch("/api/v1/billing/topup", {
1270
+ method: "POST",
1271
+ body: { credits: cr },
1272
+ });
1273
+ console.log(JSON.stringify(up, null, 2));
1274
+ }
1275
+ return;
1276
+ }
1277
+ if (choice === "4") {
1278
+ const provider = (await promptUserLine("OAuth Provider (github, gmail, slack, notion, linear): ")) || "github";
1279
+ const conn = await remoteFetch("/api/v1/connections", {
1280
+ method: "POST",
1281
+ body: { provider },
1282
+ });
1283
+ console.log(JSON.stringify(conn, null, 2));
1284
+ return;
1285
+ }
1286
+ if (choice === "5") {
1287
+ const dbPrompt = (await promptUserLine("Database to provision (e.g. 'Provision Turso database my-app-db'): ")) || "Provision Turso database my-app-db";
1288
+ const res = await remoteFetch("/api/v1/do", {
1289
+ method: "POST",
1290
+ body: { prompt: dbPrompt, autoExecute: true, approveRisky: true },
1291
+ });
1292
+ console.log(JSON.stringify(res, null, 2));
1293
+ return;
1294
+ }
1295
+ const res = await remoteFetch("/api/v1/do", {
1296
+ method: "POST",
1297
+ body: { prompt: choice, autoExecute: true, approveRisky: true },
1298
+ });
1299
+ console.log(JSON.stringify(res, null, 2));
1300
+ return;
1301
+ }
1302
+ const { store, engine, ctx } = getLocalContext();
1303
+ const freshWs = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
1304
+ const byokKeys = store.listByokKeys(ctx.workspace.id).filter((k) => k.configured);
1305
+ const conns = store.listConnections(ctx.workspace.id);
1306
+ const resources = store.listResources(ctx.workspace.id);
1307
+ const gov = engine.governance.getStatus(freshWs);
1308
+ if (flags.json || flags["non-interactive"] || flags.once) {
1309
+ console.log(JSON.stringify({
1310
+ ok: true,
1311
+ tui: "ready",
1312
+ mode: "tui_snapshot",
1313
+ workspace: {
1314
+ id: freshWs.id,
1315
+ name: freshWs.name,
1316
+ slug: freshWs.slug,
1317
+ tier: freshWs.tier,
1318
+ creditsBalance: freshWs.creditsBalance,
1319
+ monthlyActionsUsed: freshWs.monthlyActionsUsed,
1320
+ monthlyActionsLimit: freshWs.monthlyActionsLimit,
1321
+ },
1322
+ governanceDid: gov.agentDid,
1323
+ byokKeys,
1324
+ activeConnections: conns.filter((c) => c.status === "active").length,
1325
+ activeResources: resources.filter((r) => r.status === "active").length,
1326
+ supportedByokProviders: Object.keys(BYOK_PROVIDER_CATALOG),
1327
+ }, null, 2));
1328
+ return;
1329
+ }
1330
+ const printBanner = () => {
1331
+ const wsNow = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
1332
+ const keysNow = store.listByokKeys(wsNow.id).filter((k) => k.configured);
1333
+ const hasSupplierByok = keysNow.some((k) => k.category === "supplier");
1334
+ console.log("\n┌──────────────────────────────────────────────────────────────────────────┐");
1335
+ console.log("│ ⚡ MUXO INTERACTIVE TUI — Execution, Cloud Infra, Memory & Governance │");
1336
+ console.log("├──────────────────────────────────────────────────────────────────────────┤");
1337
+ console.log(`│ Workspace : ${wsNow.name.padEnd(28)} Tier : ${wsNow.tier.toUpperCase().padEnd(16)}│`);
1338
+ console.log(`│ Credits : ${String(wsNow.creditsBalance).padEnd(28)} Funding : ${(hasSupplierByok ? "BYOK (0 cr)" : "muxo_funded").padEnd(16)}│`);
1339
+ console.log(`│ BYOK Keys : ${String(keysNow.length).padEnd(28)} DID : ${gov.agentDid.slice(0, 16)}│`);
1340
+ console.log("├──────────────────────────────────────────────────────────────────────────┤");
1341
+ console.log("│ [1] ✨ Run in Plain English (Natural Language Router: muxo do) │");
1342
+ console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Gemini, Firecrawl...) │");
1343
+ console.log("│ [3] 💳 Billing: Top Up +1,000 Credits or Upgrade to Starter Plan │");
1344
+ console.log("│ [4] 🔗 Connect OAuth App (GitHub, Gmail, Slack, Notion, Linear) │");
1345
+ console.log("│ [5] ☁️ Provision Cloud Database (Turso SQLite, Neon Postgres, Redis) │");
1346
+ console.log("│ [q] Exit TUI │");
1347
+ console.log("└──────────────────────────────────────────────────────────────────────────┘");
1348
+ };
1349
+ printBanner();
1350
+ const choice = (await promptUserLine("\nSelect an option [1-5, q] (or type any natural language command): ")).trim();
1351
+ if (!choice || choice.toLowerCase() === "q" || choice.toLowerCase() === "exit") {
1352
+ console.log("Exited Muxo TUI.");
1353
+ return;
1354
+ }
1355
+ if (choice === "1") {
1356
+ const nlPrompt = await promptUserLine("Ask Muxo in plain English: ");
1357
+ if (nlPrompt) {
1358
+ const res = await engine.doNaturalLanguage(ctx, { prompt: nlPrompt, autoApprove: true });
1359
+ console.log(JSON.stringify(res, null, 2));
1360
+ }
1361
+ return;
1362
+ }
1363
+ if (choice === "2") {
1364
+ const provider = (await promptUserLine("Provider (openai, anthropic, openrouter, gemini, composio, stripe, supermemory, firecrawl, exa): ")).toLowerCase();
1365
+ const secretKey = await promptUserLine(`Paste API key for ${provider}: `);
1366
+ if (provider && secretKey) {
1367
+ const saved = store.setByokKey(ctx.workspace.id, provider, secretKey);
1368
+ console.log(JSON.stringify({ ok: true, saved }, null, 2));
1369
+ }
1370
+ return;
1371
+ }
1372
+ if (choice === "3") {
1373
+ const action = (await promptUserLine("Type 'upgrade' for Starter Plan or credit amount (default 1000): ")).toLowerCase();
1374
+ if (action === "upgrade") {
1375
+ const up = store.upgradeWorkspaceToStarter({
1376
+ workspaceId: ctx.workspace.id,
1377
+ referenceId: `tui_upgrade_${Date.now()}`,
1378
+ bonusCredits: 1000,
1379
+ });
1380
+ console.log(JSON.stringify({ ok: true, upgraded: true, workspace: up }, null, 2));
1381
+ }
1382
+ else {
1383
+ const cr = Math.max(100, Number(action) || 1000);
1384
+ const up = store.addWorkspaceCredits({
1385
+ workspaceId: ctx.workspace.id,
1386
+ credits: cr,
1387
+ referenceId: `tui_topup_${Date.now()}`,
1388
+ description: `TUI credit top-up (+${cr} credits)`,
1389
+ });
1390
+ console.log(JSON.stringify({ ok: true, creditsAdded: cr, creditsBalance: up.creditsBalance }, null, 2));
1391
+ }
1392
+ return;
1393
+ }
1394
+ if (choice === "4") {
1395
+ const provider = (await promptUserLine("OAuth Provider (github, gmail, slack, notion, linear): ")) || "github";
1396
+ const conn = await engine.startConnection(ctx, { provider });
1397
+ console.log(JSON.stringify({ ok: true, connection: conn }, null, 2));
1398
+ return;
1399
+ }
1400
+ if (choice === "5") {
1401
+ const dbPrompt = (await promptUserLine("Database to provision (e.g. 'Provision Turso database my-app-db'): ")) || "Provision Turso database my-app-db";
1402
+ const res = await engine.doNaturalLanguage(ctx, { prompt: dbPrompt, autoApprove: true });
1403
+ console.log(JSON.stringify(res, null, 2));
1404
+ return;
1405
+ }
1406
+ // Otherwise treat whatever they typed as a natural language command!
1407
+ const res = await engine.doNaturalLanguage(ctx, { prompt: choice, autoApprove: true });
1408
+ console.log(JSON.stringify(res, null, 2));
1409
+ return;
1410
+ }
485
1411
  if (cmd === "memory") {
486
1412
  const sub = positional[1];
487
1413
  if (sub === "add") {
488
1414
  const content = positional.slice(2).join(" ") || String(flags.content || "");
489
1415
  if (!content)
490
1416
  throw new Error("Usage: muxo memory add <text>");
1417
+ if (remoteUrl) {
1418
+ const res = await remoteFetch("/api/v1/memory", {
1419
+ method: "POST",
1420
+ body: {
1421
+ content,
1422
+ title: flags.title ? String(flags.title) : content.slice(0, 60),
1423
+ },
1424
+ });
1425
+ console.log(JSON.stringify(res, null, 2));
1426
+ return;
1427
+ }
1428
+ const { engine, ctx } = getLocalContext();
491
1429
  const entry = await engine.addWorkspaceMemory(ctx, {
492
1430
  content,
493
1431
  title: flags.title ? String(flags.title) : content.slice(0, 60),
@@ -495,6 +1433,16 @@ async function runCli() {
495
1433
  console.log(JSON.stringify({ ok: true, memory: entry }, null, 2));
496
1434
  return;
497
1435
  }
1436
+ if (remoteUrl) {
1437
+ const res = await remoteFetch("/api/v1/memory");
1438
+ console.log(JSON.stringify({
1439
+ ok: true,
1440
+ ...res,
1441
+ localMemories: res.records || [],
1442
+ }, null, 2));
1443
+ return;
1444
+ }
1445
+ const { engine, ctx } = getLocalContext();
498
1446
  const query = sub === "recall" || sub === "search"
499
1447
  ? positional.slice(2).join(" ") || flags.query || undefined
500
1448
  : positional.slice(1).join(" ") || flags.query || undefined;
@@ -507,6 +1455,18 @@ async function runCli() {
507
1455
  return;
508
1456
  }
509
1457
  if (cmd === "governance") {
1458
+ if (remoteUrl) {
1459
+ const res = await remoteFetch("/api/v1/governance");
1460
+ console.log(JSON.stringify({
1461
+ ok: true,
1462
+ did: res.governance?.agentDid,
1463
+ chainValid: res.governance?.auditIntegrityValid ?? true,
1464
+ ringName: "Ring 2 (Standard)",
1465
+ ...res,
1466
+ }, null, 2));
1467
+ return;
1468
+ }
1469
+ const { engine, ctx } = getLocalContext();
510
1470
  const status = engine.governance.getGovernanceStatus(ctx.workspace);
511
1471
  const summary = engine.governance.getStatus(ctx.workspace);
512
1472
  console.log(JSON.stringify({
@@ -542,35 +1502,79 @@ async function runCli() {
542
1502
  });
543
1503
  return;
544
1504
  }
1505
+ // Natural Language fallback: if user passed plain English directly (`muxo "Audit https://muxo.ai for SEO"`)
1506
+ if (cmd !== "help" && !flags.help && !flags.h && positional.length > 0) {
1507
+ const promptText = positional.join(" ").trim();
1508
+ const planOnly = flags["plan-only"] === true || flags.plan === true;
1509
+ const autoApprove = flags["no-approve"] ? false : true;
1510
+ const fundingMode = flags.byok || flags.funding === "byok"
1511
+ ? "byok"
1512
+ : flags.funding === "muxo_funded"
1513
+ ? "muxo_funded"
1514
+ : undefined;
1515
+ if (remoteUrl) {
1516
+ const data = await remoteFetch("/api/v1/do", {
1517
+ method: "POST",
1518
+ body: {
1519
+ prompt: promptText,
1520
+ autoExecute: !planOnly,
1521
+ approveRisky: autoApprove,
1522
+ fundingMode,
1523
+ },
1524
+ });
1525
+ console.log(JSON.stringify(data, null, 2));
1526
+ return;
1527
+ }
1528
+ const { engine, ctx } = getLocalContext();
1529
+ const result = await engine.doNaturalLanguage(ctx, {
1530
+ prompt: promptText,
1531
+ autoApprove,
1532
+ planOnly,
1533
+ fundingMode,
1534
+ });
1535
+ console.log(JSON.stringify(result, null, 2));
1536
+ return;
1537
+ }
545
1538
  console.log(`
546
- Muxo CLI — Unified Execution, Provisioning & Governance Platform
1539
+ Muxo CLI — Unified Agent Execution, Cloud Infrastructure, Memory & Safety Platform
1540
+
1541
+ Natural Language & Interactive TUI:
1542
+ muxo setup 1-command auto-signup + MCP setup (.mcp.json + ~/.muxo/config.json)
1543
+ muxo do "<instruction>" Execute any goal in plain English (LLM/semantic router -> plan -> execute)
1544
+ muxo ask "<instruction>" [--plan-only] Alias for 'muxo do' (pass --plan-only to inspect plan before running)
1545
+ muxo tui Launch interactive Terminal UI dashboard (BYOK, Billing, OAuth, Infra)
1546
+ muxo byok [list|set|delete] [<provider>] Manage BYOK API keys in AES-256-GCM Vault (0 credit cost when active)
547
1547
 
548
- Commands:
1548
+ Core Commands:
549
1549
  muxo login [--api-key <key> | --signup] Sign in or create an autonomous workspace (~/.muxo/config.json)
550
1550
  muxo logout Remove saved CLI authentication config
551
1551
  muxo claim <owner_email> Generate a human owner claim link for this workspace
552
- muxo whoami Show workspace profile, plan, DID, and credit balance
553
- muxo search <query> [--lane <lane>] Search across Instant, Composio Apps, and Stripe Projects
1552
+ muxo whoami Show workspace profile, plan, agent identity, and credit balance
1553
+ muxo search <query> [--lane <lane>] Search across Instant Tools, Connected Apps, Cloud Infra & Billing
554
1554
  muxo inspect <tool_slug> Inspect tool schema, risk level, and credit cost
555
- muxo connections [--sync true|false] List Composio OAuth & Stripe Projects provider connections
1555
+ muxo connections [--sync true|false] List Connected Apps & Cloud Infrastructure provider connections
556
1556
  muxo link <provider> [--wait] Start OAuth handoff for a service and optionally wait
557
1557
  muxo connect <provider> [--wait] Alias for 'muxo link'
558
1558
  muxo wait <provider> [--timeout 15] Poll until a pending OAuth connection handoff becomes active
559
- muxo plan <tool_slug> --input '{...}' Create a governed execution plan (plan_*)
1559
+ muxo plan <tool_slug> --input '{...}' Create a governed execution plan (accepts natural language too)
560
1560
  muxo approve <plan_id> Approve a pending high-risk plan
561
- muxo run <plan_id> Execute an approved plan and issue a signed receipt
1561
+ muxo run <plan_id> Execute an approved plan and issue a verifiable audit record
562
1562
  muxo exec <tool_slug> --input '{...}' Plan and execute in one step (pass --approve for high-risk)
563
- muxo resources List provisioned Stripe Projects infrastructure resources
1563
+ muxo resources List provisioned Muxo Cloud Infrastructure resources
564
1564
  muxo env pull [--out .env.muxo] Decrypt and write provisioned credentials to local .env
565
- muxo receipts [<receipt_id>] List or cryptographically verify execution receipts
566
- muxo billing [status|topup|checkout] Buy Muxo credits & inspect User -> Muxo -> Supplier settlements
567
- muxo memory [<query>] Recall Supermemory v5 workspace brain & hybrid memories
568
- muxo governance Inspect Microsoft Agent Governance Ed25519 DID & audit chain
1565
+ muxo receipts [<receipt_id>] List or verify execution audit records
1566
+ muxo billing [status|topup|upgrade|checkout] Manage credits, upgrade to Starter, or inspect settlements
1567
+ muxo memory [<query>] Recall Muxo Workspace Memory & hybrid context
1568
+ muxo governance Inspect Muxo Safety & Approvals identity and audit chain
569
1569
  muxo serve [--port 4310] Start unified HTTP Console, Landing, OAuth Callback & MCP server
570
- muxo mcp Run stdio MCP server (19 canonical tools)
1570
+ muxo mcp Run stdio MCP server (20 canonical tools)
571
1571
  `);
572
1572
  }
573
- runCli().catch((err) => {
1573
+ runCli()
1574
+ .then(() => {
1575
+ process.exit(0);
1576
+ })
1577
+ .catch((err) => {
574
1578
  console.error("Error:", err?.message || err);
575
1579
  process.exit(1);
576
1580
  });