@muxoai/platform 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude-plugin/marketplace.json +19 -4
  3. package/.codex/config.toml +9 -2
  4. package/README.md +54 -19
  5. package/dist/cli.js +780 -94
  6. package/dist/engine.d.ts +1 -0
  7. package/dist/engine.js +52 -4
  8. package/dist/mcp.d.ts +6 -1
  9. package/dist/mcp.js +108 -22
  10. package/dist/nl.js +60 -36
  11. package/dist/providers/composio-instant.d.ts +1 -0
  12. package/dist/providers/composio-instant.js +6 -5
  13. package/dist/providers/composio-sdk.d.ts +1 -0
  14. package/dist/providers/composio-sdk.js +6 -2
  15. package/dist/providers/stripe-billing.js +14 -4
  16. package/dist/providers/stripe-projects.js +61 -4
  17. package/dist/providers/supermemory.d.ts +2 -0
  18. package/dist/providers/supermemory.js +58 -40
  19. package/dist/server.js +85 -27
  20. package/dist/store.d.ts +1 -0
  21. package/dist/store.js +20 -4
  22. package/dist/ui/console-html.js +134 -114
  23. package/package.json +1 -1
  24. package/plugins/muxo/.app.json +1 -1
  25. package/plugins/muxo/.claude-plugin/plugin.json +2 -2
  26. package/plugins/muxo/.codex-plugin/plugin.json +7 -5
  27. package/plugins/muxo/.mcp.json +1 -33
  28. package/plugins/muxo/assets/logo.png +0 -0
  29. package/plugins/muxo/hooks/session-start.sh +1 -1
  30. package/plugins/muxo/plugin.json +63 -6
  31. package/plugins/muxo/skills/muxo/SKILL.md +92 -14
  32. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  33. package/plugins/muxo/skills/muxo/references/cli.md +4 -2
  34. package/plugins/muxo/skills/muxo/references/mcp.md +15 -9
  35. package/plugins/muxo/skills/setup/SKILL.md +8 -2
  36. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
package/dist/cli.js CHANGED
@@ -70,37 +70,104 @@ function parseFlags(args) {
70
70
  return { positional, flags };
71
71
  }
72
72
  async function runCli() {
73
- const config = getConfig();
74
- const store = new MuxoStore(config.dbPath, config.vaultMasterKey);
75
- const engine = new MuxoEngine(config, store);
76
73
  const { positional, flags } = parseFlags(process.argv.slice(2));
77
74
  const cmd = positional[0] || "help";
78
75
  if (flags.version || flags.v || cmd === "version") {
79
76
  console.log(flags.json
80
- ? JSON.stringify({ ok: true, name: "@muxoai/cli", version: "1.3.0" }, null, 2)
81
- : "1.3.0");
77
+ ? JSON.stringify({ ok: true, name: "@muxoai/cli", version: "1.3.1" }, null, 2)
78
+ : "1.3.1");
82
79
  return;
83
80
  }
84
81
  const savedCliConfig = readCliAuthConfig();
85
- const apiKeyFlag = (typeof flags["api-key"] === "string" ? flags["api-key"] : "") ||
82
+ let apiKeyFlag = (typeof flags["api-key"] === "string" ? flags["api-key"] : "") ||
86
83
  process.env.MUXO_API_KEY ||
87
84
  savedCliConfig?.apiKey ||
88
85
  "";
89
- const tokenAuth = apiKeyFlag ? store.resolveAuthToken(apiKeyFlag) : null;
90
- const fallback = store.ensureDefaultWorkspace();
91
- const ctx = tokenAuth
92
- ? {
93
- user: tokenAuth.user,
94
- workspace: tokenAuth.workspace,
95
- actorId: tokenAuth.actorId,
96
- authType: tokenAuth.authType,
97
- }
98
- : {
99
- user: fallback.user,
100
- workspace: fallback.workspace,
101
- actorId: `cli:${fallback.user.id}`,
102
- 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",
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(),
103
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
+ };
104
171
  // CLI Login / Autonomous External Signup (`muxo login`)
105
172
  if (cmd === "login" || cmd === "signup") {
106
173
  const explicitKey = typeof flags["api-key"] === "string"
@@ -109,6 +176,34 @@ async function runCli() {
109
176
  ? positional[1]
110
177
  : "";
111
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();
112
207
  const resolved = store.resolveAuthToken(explicitKey);
113
208
  if (!resolved) {
114
209
  throw new Error("Invalid Muxo API key supplied to 'muxo login'.");
@@ -137,6 +232,23 @@ async function runCli() {
137
232
  ? flags.name
138
233
  : positional[1] || "Muxo CLI Agent";
139
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();
140
252
  const created = store.createAutonomousAgentSignup({
141
253
  agentName,
142
254
  harness,
@@ -168,11 +280,113 @@ async function runCli() {
168
280
  console.log(JSON.stringify({ ok: true, authenticated: false, configPath: cfgPath }, null, 2));
169
281
  return;
170
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
+ }
171
369
  if (cmd === "claim") {
172
370
  const email = positional[1] || (typeof flags.email === "string" ? flags.email : "");
173
371
  if (!email || !email.includes("@")) {
174
372
  throw new Error("Usage: muxo claim <owner_email>");
175
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();
176
390
  const claim = store.createAgentClaim({
177
391
  workspaceId: ctx.workspace.id,
178
392
  email,
@@ -183,11 +397,22 @@ async function runCli() {
183
397
  }
184
398
  // Stdio MCP Server mode (`muxo mcp`)
185
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
+ }
186
410
  const rl = readline.createInterface({
187
411
  input: process.stdin,
188
412
  terminal: false,
189
413
  });
190
414
  let queue = Promise.resolve();
415
+ let mcpSessionId = "";
191
416
  rl.on("line", (line) => {
192
417
  const trimmed = line.trim();
193
418
  if (!trimmed)
@@ -195,6 +420,35 @@ async function runCli() {
195
420
  queue = queue.then(async () => {
196
421
  try {
197
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();
198
452
  const response = await handleJsonRpcMcpRequest(engine, ctx, rpc);
199
453
  if (response) {
200
454
  process.stdout.write(JSON.stringify(response) + "\n");
@@ -220,12 +474,16 @@ async function runCli() {
220
474
  return;
221
475
  }
222
476
  if (cmd === "harness") {
477
+ let workspaceId = savedCliConfig?.workspaceId || "ws_muxo_default";
478
+ if (!remoteUrl) {
479
+ workspaceId = getLocalContext().ctx.workspace.id;
480
+ }
223
481
  console.log(JSON.stringify({
224
482
  ok: true,
225
- version: "1.3.0",
483
+ version: "1.3.1",
226
484
  passed: 7,
227
485
  total: 7,
228
- workspaceId: ctx.workspace.id,
486
+ workspaceId,
229
487
  data: {
230
488
  mcpServers: {
231
489
  muxo: {
@@ -234,7 +492,7 @@ async function runCli() {
234
492
  },
235
493
  },
236
494
  remoteMcp: {
237
- url: "https://api.muxo.ai/mcp",
495
+ url: `${remoteUrl || "https://api.muxo.ai"}/mcp`,
238
496
  transport: "streamable-http",
239
497
  },
240
498
  },
@@ -242,6 +500,47 @@ async function runCli() {
242
500
  return;
243
501
  }
244
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();
245
544
  const org = engine.getOrganization(ctx);
246
545
  console.log(JSON.stringify({
247
546
  ok: true,
@@ -265,10 +564,31 @@ async function runCli() {
265
564
  if (cmd === "search") {
266
565
  const query = positional.slice(1).join(" ") || flags.query || "";
267
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();
268
588
  const res = await engine.searchTools({
269
589
  query,
270
590
  lane,
271
- limit: flags.limit ? Number(flags.limit) : 20,
591
+ limit,
272
592
  });
273
593
  if (flags.json || !process.stdout.isTTY) {
274
594
  console.log(JSON.stringify({ ok: true, count: res.total, ...res }, null, 2));
@@ -285,11 +605,34 @@ async function runCli() {
285
605
  const slug = positional[1];
286
606
  if (!slug)
287
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();
288
614
  const tool = await engine.inspectTool(slug);
289
615
  console.log(JSON.stringify(tool, null, 2));
290
616
  return;
291
617
  }
292
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();
293
636
  const status = await engine.connectionStatus(ctx, {
294
637
  provider: positional[1],
295
638
  syncLive: flags.sync !== "false",
@@ -301,6 +644,37 @@ async function runCli() {
301
644
  const provider = positional[1];
302
645
  if (!provider)
303
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();
304
678
  const res = await engine.startConnection(ctx, {
305
679
  provider,
306
680
  lane: flags.lane,
@@ -323,6 +697,24 @@ async function runCli() {
323
697
  const provider = positional[1];
324
698
  if (!provider)
325
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();
326
718
  if (flags["simulate-callback"]) {
327
719
  await engine.handleOAuthCallback({
328
720
  workspaceId: ctx.workspace.id,
@@ -330,7 +722,6 @@ async function runCli() {
330
722
  status: "active",
331
723
  });
332
724
  }
333
- const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
334
725
  const waited = await engine.waitForConnection(ctx, {
335
726
  provider,
336
727
  timeoutSeconds,
@@ -344,6 +735,15 @@ async function runCli() {
344
735
  throw new Error("Usage: muxo plan <tool_slug> [--input '{...}']");
345
736
  const input = flags.input ? JSON.parse(String(flags.input)) : {};
346
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();
347
747
  const planned = await engine.planAction(ctx, { toolSlug, input, reason });
348
748
  console.log(JSON.stringify(planned, null, 2));
349
749
  return;
@@ -352,6 +752,12 @@ async function runCli() {
352
752
  const planId = positional[1];
353
753
  if (!planId)
354
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();
355
761
  const approved = engine.approvePlan(ctx, planId, "approved");
356
762
  console.log(JSON.stringify(approved, null, 2));
357
763
  return;
@@ -360,6 +766,12 @@ async function runCli() {
360
766
  const planId = positional[1];
361
767
  if (!planId)
362
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();
363
775
  const plan = engine.getPlan(ctx, planId);
364
776
  const result = await engine.executePlanForRisk(ctx, planId, plan.riskLevel);
365
777
  console.log(JSON.stringify(result, null, 2));
@@ -369,6 +781,54 @@ async function runCli() {
369
781
  let toolSlug = positional[1];
370
782
  if (!toolSlug)
371
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();
372
832
  if (cmd === "provision" && !toolSlug.includes(".")) {
373
833
  const searched = await engine.searchTools({
374
834
  query: toolSlug,
@@ -382,7 +842,6 @@ async function runCli() {
382
842
  toolSlug = createTool.slug;
383
843
  }
384
844
  }
385
- const input = flags.input ? JSON.parse(String(flags.input)) : {};
386
845
  const planned = await engine.planAction(ctx, {
387
846
  toolSlug,
388
847
  input,
@@ -416,11 +875,40 @@ async function runCli() {
416
875
  return;
417
876
  }
418
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();
419
884
  console.log(JSON.stringify(store.listResources(ctx.workspace.id), null, 2));
420
885
  return;
421
886
  }
422
887
  if (cmd === "env" && positional[1] === "pull") {
423
- 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();
424
912
  const workdirSecrets = await engine.stripeProjects
425
913
  .pullEnvVarsFromWorkdir()
426
914
  .catch(() => ({}));
@@ -431,7 +919,6 @@ async function runCli() {
431
919
  ...Object.entries(merged).map(([k, v]) => `${k}=${JSON.stringify(v)}`),
432
920
  "",
433
921
  ];
434
- const resolvedPath = path.resolve(process.cwd(), outFile);
435
922
  fs.writeFileSync(resolvedPath, lines.join("\n"), { mode: 0o600 });
436
923
  const keys = Object.keys(merged);
437
924
  console.log(JSON.stringify({
@@ -445,6 +932,18 @@ async function runCli() {
445
932
  }
446
933
  if (cmd === "receipts") {
447
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();
448
947
  if (receiptId) {
449
948
  console.log(JSON.stringify(engine.getReceipt(ctx, receiptId), null, 2));
450
949
  }
@@ -457,6 +956,15 @@ async function runCli() {
457
956
  const sub = cmd === "topup" ? "topup" : positional[1] || "status";
458
957
  if (sub === "topup") {
459
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();
460
968
  const usdPaid = (credits * 0.01).toFixed(2);
461
969
  const refId = `cli_topup_${Date.now()}`;
462
970
  const updated = store.addWorkspaceCredits({
@@ -476,6 +984,15 @@ async function runCli() {
476
984
  return;
477
985
  }
478
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();
479
996
  const updated = store.upgradeWorkspaceToStarter({
480
997
  workspaceId: ctx.workspace.id,
481
998
  referenceId: `cli_upgrade_${Date.now()}`,
@@ -499,6 +1016,15 @@ async function runCli() {
499
1016
  ? "starter_subscription"
500
1017
  : "credit_topup";
501
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();
502
1028
  const session = await engine.stripeBilling.createCheckoutSession({
503
1029
  workspace: ctx.workspace,
504
1030
  customerEmail: ctx.user.email,
@@ -513,10 +1039,25 @@ async function runCli() {
513
1039
  const sessionId = positional[2] || String(flags.session || "");
514
1040
  if (!sessionId)
515
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();
516
1051
  const verified = await engine.stripeBilling.verifyAndFulfillCheckoutSession(sessionId);
517
1052
  console.log(JSON.stringify({ ok: true, ...verified }, null, 2));
518
1053
  return;
519
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();
520
1061
  const freshWs = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
521
1062
  console.log(JSON.stringify({
522
1063
  ok: true,
@@ -532,7 +1073,6 @@ async function runCli() {
532
1073
  return;
533
1074
  }
534
1075
  if (cmd === "byok" || cmd === "keys") {
535
- const remoteUrl = typeof flags.url === "string" ? flags.url.replace(/\/+$/, "") : "";
536
1076
  const sub = positional[1] || "list";
537
1077
  if (sub === "set" || sub === "add" || sub === "prompt") {
538
1078
  let provider = (positional[2] || String(flags.provider || "")).trim().toLowerCase();
@@ -552,17 +1092,14 @@ async function runCli() {
552
1092
  throw new Error(`API key is required for provider '${provider}'.`);
553
1093
  }
554
1094
  if (remoteUrl) {
555
- const res = await fetch(`${remoteUrl}/api/v1/byok`, {
1095
+ const data = await remoteFetch("/api/v1/byok", {
556
1096
  method: "POST",
557
- headers: {
558
- "Content-Type": "application/json",
559
- ...(apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {}),
560
- },
561
- body: JSON.stringify({ provider, secretKey }),
1097
+ body: { provider, secretKey },
562
1098
  });
563
- console.log(JSON.stringify(await res.json(), null, 2));
1099
+ console.log(JSON.stringify(data, null, 2));
564
1100
  return;
565
1101
  }
1102
+ const { store, ctx } = getLocalContext();
566
1103
  const record = store.setByokKey(ctx.workspace.id, provider, secretKey);
567
1104
  console.log(JSON.stringify({
568
1105
  ok: true,
@@ -577,13 +1114,11 @@ async function runCli() {
577
1114
  if (!provider)
578
1115
  throw new Error("Usage: muxo byok delete <provider>");
579
1116
  if (remoteUrl) {
580
- const res = await fetch(`${remoteUrl}/api/v1/byok/${encodeURIComponent(provider)}`, {
581
- method: "DELETE",
582
- headers: apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {},
583
- });
584
- console.log(JSON.stringify(await res.json(), null, 2));
1117
+ const data = await remoteFetch(`/api/v1/byok/${encodeURIComponent(provider)}`, { method: "DELETE" });
1118
+ console.log(JSON.stringify(data, null, 2));
585
1119
  return;
586
1120
  }
1121
+ const { store, ctx } = getLocalContext();
587
1122
  store.deleteByokKey(ctx.workspace.id, provider);
588
1123
  console.log(JSON.stringify({
589
1124
  ok: true,
@@ -594,12 +1129,11 @@ async function runCli() {
594
1129
  return;
595
1130
  }
596
1131
  if (remoteUrl) {
597
- const res = await fetch(`${remoteUrl}/api/v1/byok`, {
598
- headers: apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {},
599
- });
600
- console.log(JSON.stringify(await res.json(), null, 2));
1132
+ const data = await remoteFetch("/api/v1/byok");
1133
+ console.log(JSON.stringify(data, null, 2));
601
1134
  return;
602
1135
  }
1136
+ const { store, ctx } = getLocalContext();
603
1137
  console.log(JSON.stringify({
604
1138
  ok: true,
605
1139
  keys: store.listByokKeys(ctx.workspace.id),
@@ -608,7 +1142,6 @@ async function runCli() {
608
1142
  return;
609
1143
  }
610
1144
  if (cmd === "do" || cmd === "ask" || cmd === "nl") {
611
- const remoteUrl = typeof flags.url === "string" ? flags.url.replace(/\/+$/, "") : "";
612
1145
  let promptText = positional.slice(1).join(" ").trim() ||
613
1146
  (typeof flags.prompt === "string" ? flags.prompt : "");
614
1147
  if (!promptText) {
@@ -617,10 +1150,6 @@ async function runCli() {
617
1150
  if (!promptText) {
618
1151
  throw new Error('Usage: muxo do "<natural language instruction>"');
619
1152
  }
620
- // If user requested --byok and has no BYOK key for the requested provider, optionally prompt
621
- if (flags.byok && typeof flags["byok-key"] === "string" && typeof flags["byok-provider"] === "string") {
622
- store.setByokKey(ctx.workspace.id, String(flags["byok-provider"]), String(flags["byok-key"]));
623
- }
624
1153
  const planOnly = flags["plan-only"] === true || flags.plan === true;
625
1154
  const autoApprove = flags["no-approve"] ? false : true;
626
1155
  const fundingMode = flags.byok || flags.funding === "byok"
@@ -629,21 +1158,33 @@ async function runCli() {
629
1158
  ? "muxo_funded"
630
1159
  : undefined;
631
1160
  if (remoteUrl) {
632
- const res = await fetch(`${remoteUrl}/api/v1/do`, {
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", {
633
1173
  method: "POST",
634
- headers: {
635
- "Content-Type": "application/json",
636
- ...(apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {}),
637
- },
638
- body: JSON.stringify({
1174
+ body: {
639
1175
  prompt: promptText,
640
- autoApprove,
641
- dryRun: planOnly,
642
- }),
1176
+ autoExecute: !planOnly,
1177
+ approveRisky: autoApprove,
1178
+ fundingMode,
1179
+ },
643
1180
  });
644
- console.log(JSON.stringify(await res.json(), null, 2));
1181
+ console.log(JSON.stringify(data, null, 2));
645
1182
  return;
646
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
+ }
647
1188
  const result = await engine.doNaturalLanguage(ctx, {
648
1189
  prompt: promptText,
649
1190
  autoApprove,
@@ -653,25 +1194,114 @@ async function runCli() {
653
1194
  console.log(JSON.stringify(result, null, 2));
654
1195
  return;
655
1196
  }
656
- if (cmd === "tui" || cmd === "setup" || cmd === "interactive") {
657
- const remoteUrl = typeof flags.url === "string" ? flags.url.replace(/\/+$/, "") : "";
658
- if (remoteUrl && (flags.json || flags["non-interactive"] || flags.once)) {
659
- const res = await fetch(`${remoteUrl}/api/v1/billing/status`, {
660
- headers: apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {},
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 },
661
1298
  });
662
- const statusData = (await res.json());
663
- console.log(JSON.stringify({
664
- ok: true,
665
- tui: "ready",
666
- mode: "tui_snapshot",
667
- workspace: statusData.workspace,
668
- byokKeys: statusData.byokKeys || [],
669
- supportedByokProviders: Object.keys(BYOK_PROVIDER_CATALOG),
670
- }, null, 2));
1299
+ console.log(JSON.stringify(res, null, 2));
671
1300
  return;
672
1301
  }
1302
+ const { store, engine, ctx } = getLocalContext();
673
1303
  const freshWs = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
674
- const byokKeys = store.listByokKeys(ctx.workspace.id);
1304
+ const byokKeys = store.listByokKeys(ctx.workspace.id).filter((k) => k.configured);
675
1305
  const conns = store.listConnections(ctx.workspace.id);
676
1306
  const resources = store.listResources(ctx.workspace.id);
677
1307
  const gov = engine.governance.getStatus(freshWs);
@@ -699,7 +1329,7 @@ async function runCli() {
699
1329
  }
700
1330
  const printBanner = () => {
701
1331
  const wsNow = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
702
- const keysNow = store.listByokKeys(ctx.workspace.id);
1332
+ const keysNow = store.listByokKeys(wsNow.id).filter((k) => k.configured);
703
1333
  const hasSupplierByok = keysNow.some((k) => k.category === "supplier");
704
1334
  console.log("\n┌──────────────────────────────────────────────────────────────────────────┐");
705
1335
  console.log("│ ⚡ MUXO INTERACTIVE TUI — Execution, Cloud Infra, Memory & Governance │");
@@ -709,7 +1339,7 @@ async function runCli() {
709
1339
  console.log(`│ BYOK Keys : ${String(keysNow.length).padEnd(28)} DID : ${gov.agentDid.slice(0, 16)}│`);
710
1340
  console.log("├──────────────────────────────────────────────────────────────────────────┤");
711
1341
  console.log("│ [1] ✨ Run in Plain English (Natural Language Router: muxo do) │");
712
- console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Composio, Stripe...) │");
1342
+ console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Gemini, Firecrawl...) │");
713
1343
  console.log("│ [3] 💳 Billing: Top Up +1,000 Credits or Upgrade to Starter Plan │");
714
1344
  console.log("│ [4] 🔗 Connect OAuth App (GitHub, Gmail, Slack, Notion, Linear) │");
715
1345
  console.log("│ [5] ☁️ Provision Cloud Database (Turso SQLite, Neon Postgres, Redis) │");
@@ -784,6 +1414,18 @@ async function runCli() {
784
1414
  const content = positional.slice(2).join(" ") || String(flags.content || "");
785
1415
  if (!content)
786
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();
787
1429
  const entry = await engine.addWorkspaceMemory(ctx, {
788
1430
  content,
789
1431
  title: flags.title ? String(flags.title) : content.slice(0, 60),
@@ -791,6 +1433,16 @@ async function runCli() {
791
1433
  console.log(JSON.stringify({ ok: true, memory: entry }, null, 2));
792
1434
  return;
793
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();
794
1446
  const query = sub === "recall" || sub === "search"
795
1447
  ? positional.slice(2).join(" ") || flags.query || undefined
796
1448
  : positional.slice(1).join(" ") || flags.query || undefined;
@@ -803,6 +1455,18 @@ async function runCli() {
803
1455
  return;
804
1456
  }
805
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();
806
1470
  const status = engine.governance.getGovernanceStatus(ctx.workspace);
807
1471
  const summary = engine.governance.getStatus(ctx.workspace);
808
1472
  console.log(JSON.stringify({
@@ -841,23 +1505,41 @@ async function runCli() {
841
1505
  // Natural Language fallback: if user passed plain English directly (`muxo "Audit https://muxo.ai for SEO"`)
842
1506
  if (cmd !== "help" && !flags.help && !flags.h && positional.length > 0) {
843
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();
844
1529
  const result = await engine.doNaturalLanguage(ctx, {
845
1530
  prompt: promptText,
846
- autoApprove: flags["no-approve"] ? false : true,
847
- planOnly: flags["plan-only"] === true || flags.plan === true,
848
- fundingMode: flags.byok || flags.funding === "byok"
849
- ? "byok"
850
- : flags.funding === "muxo_funded"
851
- ? "muxo_funded"
852
- : undefined,
1531
+ autoApprove,
1532
+ planOnly,
1533
+ fundingMode,
853
1534
  });
854
1535
  console.log(JSON.stringify(result, null, 2));
855
1536
  return;
856
1537
  }
857
1538
  console.log(`
858
- Muxo CLI — Human & LLM Friendly Execution, Infrastructure, BYOK & Governance Platform
1539
+ Muxo CLI — Unified Agent Execution, Cloud Infrastructure, Memory & Safety Platform
859
1540
 
860
1541
  Natural Language & Interactive TUI:
1542
+ muxo setup 1-command auto-signup + MCP setup (.mcp.json + ~/.muxo/config.json)
861
1543
  muxo do "<instruction>" Execute any goal in plain English (LLM/semantic router -> plan -> execute)
862
1544
  muxo ask "<instruction>" [--plan-only] Alias for 'muxo do' (pass --plan-only to inspect plan before running)
863
1545
  muxo tui Launch interactive Terminal UI dashboard (BYOK, Billing, OAuth, Infra)
@@ -867,28 +1549,32 @@ Core Commands:
867
1549
  muxo login [--api-key <key> | --signup] Sign in or create an autonomous workspace (~/.muxo/config.json)
868
1550
  muxo logout Remove saved CLI authentication config
869
1551
  muxo claim <owner_email> Generate a human owner claim link for this workspace
870
- muxo whoami Show workspace profile, plan, DID, and credit balance
871
- muxo search <query> [--lane <lane>] Search across Instant, Composio Apps, Stripe Projects & Billing
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
872
1554
  muxo inspect <tool_slug> Inspect tool schema, risk level, and credit cost
873
- 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
874
1556
  muxo link <provider> [--wait] Start OAuth handoff for a service and optionally wait
875
1557
  muxo connect <provider> [--wait] Alias for 'muxo link'
876
1558
  muxo wait <provider> [--timeout 15] Poll until a pending OAuth connection handoff becomes active
877
1559
  muxo plan <tool_slug> --input '{...}' Create a governed execution plan (accepts natural language too)
878
1560
  muxo approve <plan_id> Approve a pending high-risk plan
879
- 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
880
1562
  muxo exec <tool_slug> --input '{...}' Plan and execute in one step (pass --approve for high-risk)
881
- muxo resources List provisioned Stripe Projects infrastructure resources
1563
+ muxo resources List provisioned Muxo Cloud Infrastructure resources
882
1564
  muxo env pull [--out .env.muxo] Decrypt and write provisioned credentials to local .env
883
- muxo receipts [<receipt_id>] List or cryptographically verify execution receipts
1565
+ muxo receipts [<receipt_id>] List or verify execution audit records
884
1566
  muxo billing [status|topup|upgrade|checkout] Manage credits, upgrade to Starter, or inspect settlements
885
- muxo memory [<query>] Recall Supermemory v5 workspace brain & hybrid memories
886
- muxo governance Inspect Microsoft Agent Governance Ed25519 DID & audit chain
1567
+ muxo memory [<query>] Recall Muxo Workspace Memory & hybrid context
1568
+ muxo governance Inspect Muxo Safety & Approvals identity and audit chain
887
1569
  muxo serve [--port 4310] Start unified HTTP Console, Landing, OAuth Callback & MCP server
888
1570
  muxo mcp Run stdio MCP server (20 canonical tools)
889
1571
  `);
890
1572
  }
891
- runCli().catch((err) => {
1573
+ runCli()
1574
+ .then(() => {
1575
+ process.exit(0);
1576
+ })
1577
+ .catch((err) => {
892
1578
  console.error("Error:", err?.message || err);
893
1579
  process.exit(1);
894
1580
  });