@muxoai/platform 1.3.0 → 1.3.2

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 (51) 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 +63 -20
  5. package/dist/clerk.d.ts +33 -0
  6. package/dist/clerk.js +198 -0
  7. package/dist/cli.js +1181 -99
  8. package/dist/engine.d.ts +22 -1
  9. package/dist/engine.js +284 -14
  10. package/dist/mcp.d.ts +6 -1
  11. package/dist/mcp.js +109 -22
  12. package/dist/nl.js +60 -36
  13. package/dist/policy.d.ts +9 -0
  14. package/dist/policy.js +73 -0
  15. package/dist/providers/composio-instant.d.ts +4 -0
  16. package/dist/providers/composio-instant.js +634 -534
  17. package/dist/providers/composio-sdk.d.ts +1 -0
  18. package/dist/providers/composio-sdk.js +6 -2
  19. package/dist/providers/microsoft-governance.d.ts +61 -2
  20. package/dist/providers/microsoft-governance.js +458 -24
  21. package/dist/providers/stripe-billing.js +14 -4
  22. package/dist/providers/stripe-projects.d.ts +4 -1
  23. package/dist/providers/stripe-projects.js +123 -12
  24. package/dist/providers/supermemory.d.ts +28 -5
  25. package/dist/providers/supermemory.js +183 -55
  26. package/dist/sdk.d.ts +167 -1
  27. package/dist/sdk.js +113 -2
  28. package/dist/server.js +258 -40
  29. package/dist/store.d.ts +26 -1
  30. package/dist/store.js +255 -6
  31. package/dist/types.d.ts +86 -0
  32. package/dist/ui/console-html.d.ts +16 -0
  33. package/dist/ui/console-html.js +2427 -2206
  34. package/dist/ui/design-tokens.d.ts +177 -0
  35. package/dist/ui/design-tokens.js +210 -0
  36. package/dist/version.d.ts +1 -0
  37. package/dist/version.js +1 -0
  38. package/package.json +2 -2
  39. package/plugins/muxo/.app.json +1 -1
  40. package/plugins/muxo/.claude-plugin/plugin.json +2 -2
  41. package/plugins/muxo/.codex-plugin/plugin.json +7 -5
  42. package/plugins/muxo/.mcp.json +1 -33
  43. package/plugins/muxo/assets/logo.png +0 -0
  44. package/plugins/muxo/hooks/session-start.sh +1 -1
  45. package/plugins/muxo/plugin.json +63 -6
  46. package/plugins/muxo/skills/muxo/SKILL.md +92 -14
  47. package/plugins/muxo/skills/muxo/agents/openai.yaml +12 -0
  48. package/plugins/muxo/skills/muxo/references/cli.md +4 -2
  49. package/plugins/muxo/skills/muxo/references/mcp.md +15 -9
  50. package/plugins/muxo/skills/setup/SKILL.md +8 -2
  51. package/plugins/muxo/skills/setup/agents/openai.yaml +12 -0
package/dist/cli.js CHANGED
@@ -6,7 +6,10 @@ 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 { sanitizePolicyPatch } from "./policy.js";
10
+ import { RING_LABELS } from "./providers/microsoft-governance.js";
9
11
  import { BYOK_PROVIDER_CATALOG, MuxoStore } from "./store.js";
12
+ import { MUXO_VERSION } from "./version.js";
10
13
  function promptUserLine(question) {
11
14
  return new Promise((resolve) => {
12
15
  const rl = readline.createInterface({
@@ -70,37 +73,104 @@ function parseFlags(args) {
70
73
  return { positional, flags };
71
74
  }
72
75
  async function runCli() {
73
- const config = getConfig();
74
- const store = new MuxoStore(config.dbPath, config.vaultMasterKey);
75
- const engine = new MuxoEngine(config, store);
76
76
  const { positional, flags } = parseFlags(process.argv.slice(2));
77
77
  const cmd = positional[0] || "help";
78
78
  if (flags.version || flags.v || cmd === "version") {
79
79
  console.log(flags.json
80
- ? JSON.stringify({ ok: true, name: "@muxoai/cli", version: "1.3.0" }, null, 2)
81
- : "1.3.0");
80
+ ? JSON.stringify({ ok: true, name: "@muxoai/cli", version: MUXO_VERSION }, null, 2)
81
+ : MUXO_VERSION);
82
82
  return;
83
83
  }
84
84
  const savedCliConfig = readCliAuthConfig();
85
- const apiKeyFlag = (typeof flags["api-key"] === "string" ? flags["api-key"] : "") ||
85
+ let apiKeyFlag = (typeof flags["api-key"] === "string" ? flags["api-key"] : "") ||
86
86
  process.env.MUXO_API_KEY ||
87
87
  savedCliConfig?.apiKey ||
88
88
  "";
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",
89
+ const explicitUrlFlag = (typeof flags.url === "string"
90
+ ? flags.url
91
+ : process.env.MUXO_API_URL || process.env.MUXO_CLOUD_URL || "").replace(/\/+$/, "");
92
+ const useLocalDb = Boolean((process.env.MUXO_DB_PATH || flags.local) && !explicitUrlFlag);
93
+ const remoteUrl = useLocalDb
94
+ ? ""
95
+ : (explicitUrlFlag || savedCliConfig?.apiUrl || "https://api.muxo.ai").replace(/\/+$/, "");
96
+ const remoteFetch = async (pathname, opts) => {
97
+ const keyToUse = opts?.apiKey ?? apiKeyFlag;
98
+ const headers = {
99
+ Accept: "application/json",
103
100
  };
101
+ if (opts?.body !== undefined) {
102
+ headers["Content-Type"] = "application/json";
103
+ }
104
+ if (keyToUse) {
105
+ headers["Authorization"] = `Bearer ${keyToUse}`;
106
+ }
107
+ const res = await fetch(`${remoteUrl}${pathname}`, {
108
+ method: opts?.method || "GET",
109
+ headers,
110
+ body: opts?.body !== undefined ? JSON.stringify(opts.body) : undefined,
111
+ });
112
+ const data = (await res.json().catch(() => ({})));
113
+ if (!res.ok && data.error) {
114
+ throw new Error(String(data.error));
115
+ }
116
+ return data;
117
+ };
118
+ const signupHostedWorkspace = async (agentName, harness) => {
119
+ const createdRemote = await remoteFetch("/api/v1/agents/signup", {
120
+ method: "POST",
121
+ body: { agentName, harness },
122
+ apiKey: "",
123
+ });
124
+ if (!createdRemote || !createdRemote.apiKey) {
125
+ throw new Error(`Hosted signup at ${remoteUrl}/api/v1/agents/signup failed to return an API key.`);
126
+ }
127
+ const authPayload = {
128
+ authenticated: true,
129
+ apiKey: String(createdRemote.apiKey),
130
+ apiUrl: remoteUrl,
131
+ workspaceId: String(createdRemote.workspaceId || createdRemote.workspace?.id || ""),
132
+ workspaceName: String(createdRemote.workspace?.name || `${agentName} Workspace`),
133
+ agentId: String(createdRemote.agentId || createdRemote.user?.id || "usr_agent"),
134
+ governanceDid: String(createdRemote.governanceDid || ""),
135
+ supermemoryNamespace: String(createdRemote.supermemoryNamespace || ""),
136
+ updatedAt: new Date().toISOString(),
137
+ };
138
+ return { authPayload, rawResponse: createdRemote };
139
+ };
140
+ const config = getConfig();
141
+ let _store = null;
142
+ let _engine = null;
143
+ const getStore = () => {
144
+ if (!_store) {
145
+ _store = new MuxoStore(config.dbPath, config.vaultMasterKey);
146
+ }
147
+ return _store;
148
+ };
149
+ const getEngine = () => {
150
+ if (!_engine) {
151
+ _engine = new MuxoEngine(config, getStore());
152
+ }
153
+ return _engine;
154
+ };
155
+ const getLocalContext = () => {
156
+ const store = getStore();
157
+ const tokenAuth = apiKeyFlag ? store.resolveAuthToken(apiKeyFlag) : null;
158
+ const fallback = store.ensureDefaultWorkspace();
159
+ const ctx = tokenAuth
160
+ ? {
161
+ user: tokenAuth.user,
162
+ workspace: tokenAuth.workspace,
163
+ actorId: tokenAuth.actorId,
164
+ authType: tokenAuth.authType,
165
+ }
166
+ : {
167
+ user: fallback.user,
168
+ workspace: fallback.workspace,
169
+ actorId: `cli:${fallback.user.id}`,
170
+ authType: "session",
171
+ };
172
+ return { store, engine: getEngine(), ctx, tokenAuth };
173
+ };
104
174
  // CLI Login / Autonomous External Signup (`muxo login`)
105
175
  if (cmd === "login" || cmd === "signup") {
106
176
  const explicitKey = typeof flags["api-key"] === "string"
@@ -109,6 +179,34 @@ async function runCli() {
109
179
  ? positional[1]
110
180
  : "";
111
181
  if (explicitKey) {
182
+ if (remoteUrl) {
183
+ const boot = await remoteFetch("/api/v1/bootstrap", {
184
+ apiKey: explicitKey,
185
+ });
186
+ const ws = boot.organization || {};
187
+ const usr = boot.profile?.user || {};
188
+ const authPayload = {
189
+ authenticated: true,
190
+ apiKey: explicitKey,
191
+ apiUrl: remoteUrl,
192
+ workspaceId: String(ws.id || boot.profile?.workspaceId || ""),
193
+ workspaceName: String(ws.name || "Muxo Workspace"),
194
+ agentId: String(usr.id || "usr_agent"),
195
+ governanceDid: String(boot.governance?.agentDid || ""),
196
+ supermemoryNamespace: String(boot.supermemory?.namespace || ""),
197
+ updatedAt: new Date().toISOString(),
198
+ };
199
+ const cfgPath = writeCliAuthConfig(authPayload);
200
+ console.log(JSON.stringify({
201
+ ok: true,
202
+ mode: "api_key",
203
+ configPath: cfgPath,
204
+ workspace: ws,
205
+ ...authPayload,
206
+ }, null, 2));
207
+ return;
208
+ }
209
+ const { store, engine } = getLocalContext();
112
210
  const resolved = store.resolveAuthToken(explicitKey);
113
211
  if (!resolved) {
114
212
  throw new Error("Invalid Muxo API key supplied to 'muxo login'.");
@@ -137,6 +235,23 @@ async function runCli() {
137
235
  ? flags.name
138
236
  : positional[1] || "Muxo CLI Agent";
139
237
  const harness = typeof flags.harness === "string" ? flags.harness : "muxo-cli";
238
+ if (remoteUrl) {
239
+ const { authPayload, rawResponse } = await signupHostedWorkspace(agentName, harness);
240
+ const cfgPath = writeCliAuthConfig(authPayload);
241
+ console.log(JSON.stringify({
242
+ ok: true,
243
+ mode: "autonomous_signup",
244
+ configPath: cfgPath,
245
+ workspace: rawResponse.workspace,
246
+ creditsBalance: rawResponse.workspace?.creditsBalance ?? 250,
247
+ mcpEndpoint: rawResponse.mcpEndpoint || `${remoteUrl}/mcp`,
248
+ skillUrl: rawResponse.skillUrl || `${remoteUrl}/skill.md`,
249
+ claimEndpoint: rawResponse.claimEndpoint || `${remoteUrl}/api/v1/agents/claim`,
250
+ ...authPayload,
251
+ }, null, 2));
252
+ return;
253
+ }
254
+ const { store, engine } = getLocalContext();
140
255
  const created = store.createAutonomousAgentSignup({
141
256
  agentName,
142
257
  harness,
@@ -168,11 +283,155 @@ async function runCli() {
168
283
  console.log(JSON.stringify({ ok: true, authenticated: false, configPath: cfgPath }, null, 2));
169
284
  return;
170
285
  }
286
+ // One-command setup (`muxo setup` / `muxo init` / `muxo install`):
287
+ // 1. Auto-creates or loads ~/.muxo/config.json with 250 starter credits (or binds an explicit --api-key)
288
+ // 2. Writes .mcp.json in target directory (or ~/.cursor/mcp.json & ~/.factory/mcp.json if present)
289
+ if (cmd === "setup" || cmd === "init" || cmd === "install") {
290
+ const explicitKey = typeof flags["api-key"] === "string"
291
+ ? flags["api-key"].trim()
292
+ : typeof flags.key === "string"
293
+ ? flags.key.trim()
294
+ : "";
295
+ let activeConfig = savedCliConfig;
296
+ if (explicitKey) {
297
+ const agentName = typeof flags.name === "string" ? flags.name : "Muxo Agent";
298
+ if (remoteUrl) {
299
+ apiKeyFlag = explicitKey;
300
+ const orgRes = (await remoteFetch("/api/v1/organization").catch(() => ({})));
301
+ const org = orgRes?.organization || orgRes?.workspace || {};
302
+ activeConfig = {
303
+ authenticated: true,
304
+ apiKey: explicitKey,
305
+ workspaceId: org.id || "ws_remote",
306
+ workspaceName: org.name || agentName,
307
+ agentId: "usr_remote",
308
+ governanceDid: org.governanceDid || `did:muxo:${org.id || "ws_remote"}`,
309
+ supermemoryNamespace: `muxo_${org.id || "ws_remote"}`,
310
+ updatedAt: new Date().toISOString(),
311
+ };
312
+ }
313
+ else {
314
+ const { store, engine } = getLocalContext();
315
+ const auth = store.resolveAuthToken(explicitKey);
316
+ if (!auth) {
317
+ throw new Error(`Invalid API key provided to muxo setup: ${explicitKey.slice(0, 12)}...`);
318
+ }
319
+ activeConfig = {
320
+ authenticated: true,
321
+ apiKey: explicitKey,
322
+ workspaceId: auth.workspace.id,
323
+ workspaceName: auth.workspace.name,
324
+ agentId: auth.user.id,
325
+ governanceDid: engine.governance.getStatus(auth.workspace).agentDid,
326
+ supermemoryNamespace: engine.supermemory.getWorkspaceNamespace(auth.workspace.id),
327
+ updatedAt: new Date().toISOString(),
328
+ };
329
+ }
330
+ writeCliAuthConfig(activeConfig);
331
+ }
332
+ else if (!activeConfig || !activeConfig.apiKey || flags.fresh) {
333
+ const agentName = typeof flags.name === "string" ? flags.name : "Muxo Agent";
334
+ if (remoteUrl) {
335
+ const { authPayload } = await signupHostedWorkspace(agentName, "muxo-setup");
336
+ activeConfig = authPayload;
337
+ }
338
+ else {
339
+ const { store, engine } = getLocalContext();
340
+ const created = store.createAutonomousAgentSignup({
341
+ agentName,
342
+ harness: "muxo-setup",
343
+ });
344
+ activeConfig = {
345
+ authenticated: true,
346
+ apiKey: created.apiKey,
347
+ workspaceId: created.workspace.id,
348
+ workspaceName: created.workspace.name,
349
+ agentId: created.user.id,
350
+ governanceDid: engine.governance.getStatus(created.workspace).agentDid,
351
+ supermemoryNamespace: engine.supermemory.getWorkspaceNamespace(created.workspace.id),
352
+ updatedAt: new Date().toISOString(),
353
+ };
354
+ }
355
+ writeCliAuthConfig(activeConfig);
356
+ }
357
+ const ensuredConfig = activeConfig;
358
+ const mcpServerUrl = remoteUrl
359
+ ? `${remoteUrl}/mcp`
360
+ : "https://api.muxo.ai/mcp";
361
+ const mcpEntry = {
362
+ type: "http",
363
+ url: mcpServerUrl,
364
+ headers: {
365
+ Authorization: `Bearer ${ensuredConfig.apiKey}`,
366
+ },
367
+ };
368
+ const targetDir = typeof flags.dir === "string" ? path.resolve(flags.dir) : process.cwd();
369
+ const writtenFiles = [];
370
+ const mergeMcpJsonFile = (filePath) => {
371
+ try {
372
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
373
+ let existing = {};
374
+ if (fs.existsSync(filePath)) {
375
+ existing = JSON.parse(fs.readFileSync(filePath, "utf8")) || {};
376
+ }
377
+ existing.mcpServers = {
378
+ ...(existing.mcpServers || {}),
379
+ muxo: mcpEntry,
380
+ };
381
+ fs.writeFileSync(filePath, JSON.stringify(existing, null, 2) + "\n");
382
+ writtenFiles.push(filePath);
383
+ }
384
+ catch {
385
+ // Ignore non-writable paths
386
+ }
387
+ };
388
+ // Always write .mcp.json in targetDir when requested or if in a project
389
+ mergeMcpJsonFile(path.join(targetDir, ".mcp.json"));
390
+ const resultPayload = {
391
+ ok: true,
392
+ workspaceId: ensuredConfig.workspaceId,
393
+ workspaceName: ensuredConfig.workspaceName,
394
+ apiKey: ensuredConfig.apiKey,
395
+ mcpUrl: mcpServerUrl,
396
+ configPath: getCliConfigPath(),
397
+ configuredFiles: writtenFiles,
398
+ };
399
+ if (flags.json || !process.stdout.isTTY) {
400
+ console.log(JSON.stringify(resultPayload, null, 2));
401
+ }
402
+ else {
403
+ console.log(`\n✓ Muxo is ready!`);
404
+ console.log(` Workspace : ${ensuredConfig.workspaceName} (${ensuredConfig.workspaceId})`);
405
+ console.log(` API Key : ${ensuredConfig.apiKey}`);
406
+ console.log(` MCP URL : ${mcpServerUrl}`);
407
+ console.log(` MCP Config: ${writtenFiles.join(", ")}`);
408
+ console.log(`\nTry it now:`);
409
+ console.log(` muxo do "check dns A records for muxo.ai"`);
410
+ console.log(` muxo tui\n`);
411
+ }
412
+ return;
413
+ }
171
414
  if (cmd === "claim") {
172
415
  const email = positional[1] || (typeof flags.email === "string" ? flags.email : "");
173
416
  if (!email || !email.includes("@")) {
174
417
  throw new Error("Usage: muxo claim <owner_email>");
175
418
  }
419
+ if (remoteUrl) {
420
+ const res = await remoteFetch("/api/v1/agents/claim", {
421
+ method: "POST",
422
+ body: { email },
423
+ });
424
+ const claimObj = res.claim || res;
425
+ console.log(JSON.stringify({
426
+ ok: true,
427
+ claim: claimObj,
428
+ ownerEmail: res.ownerEmail || claimObj?.targetEmail || email,
429
+ claimSlug: res.claimSlug || claimObj?.claimCode,
430
+ ...claimObj,
431
+ }, null, 2));
432
+ return;
433
+ }
434
+ const { store, ctx } = getLocalContext();
176
435
  const claim = store.createAgentClaim({
177
436
  workspaceId: ctx.workspace.id,
178
437
  email,
@@ -183,11 +442,22 @@ async function runCli() {
183
442
  }
184
443
  // Stdio MCP Server mode (`muxo mcp`)
185
444
  if (cmd === "mcp") {
445
+ if (remoteUrl && !apiKeyFlag) {
446
+ try {
447
+ const { authPayload } = await signupHostedWorkspace("Muxo MCP Agent", "muxo-mcp-stdio");
448
+ writeCliAuthConfig(authPayload);
449
+ apiKeyFlag = authPayload.apiKey;
450
+ }
451
+ catch {
452
+ // Proceed with remote default workspace if signup fails
453
+ }
454
+ }
186
455
  const rl = readline.createInterface({
187
456
  input: process.stdin,
188
457
  terminal: false,
189
458
  });
190
459
  let queue = Promise.resolve();
460
+ let mcpSessionId = "";
191
461
  rl.on("line", (line) => {
192
462
  const trimmed = line.trim();
193
463
  if (!trimmed)
@@ -195,6 +465,35 @@ async function runCli() {
195
465
  queue = queue.then(async () => {
196
466
  try {
197
467
  const rpc = JSON.parse(trimmed);
468
+ if (remoteUrl) {
469
+ const headers = {
470
+ "Content-Type": "application/json",
471
+ Accept: "application/json",
472
+ };
473
+ if (apiKeyFlag) {
474
+ headers["Authorization"] = `Bearer ${apiKeyFlag}`;
475
+ }
476
+ if (mcpSessionId) {
477
+ headers["Mcp-Session-Id"] = mcpSessionId;
478
+ }
479
+ const res = await fetch(`${remoteUrl}/mcp`, {
480
+ method: "POST",
481
+ headers,
482
+ body: JSON.stringify(rpc),
483
+ });
484
+ const sid = res.headers.get("mcp-session-id");
485
+ if (sid)
486
+ mcpSessionId = sid;
487
+ if (rpc.id !== undefined && res.status !== 202 && res.status !== 204) {
488
+ const text = await res.text();
489
+ if (text.trim()) {
490
+ const parsed = JSON.parse(text);
491
+ process.stdout.write(JSON.stringify(parsed) + "\n");
492
+ }
493
+ }
494
+ return;
495
+ }
496
+ const { engine, ctx } = getLocalContext();
198
497
  const response = await handleJsonRpcMcpRequest(engine, ctx, rpc);
199
498
  if (response) {
200
499
  process.stdout.write(JSON.stringify(response) + "\n");
@@ -220,12 +519,194 @@ async function runCli() {
220
519
  return;
221
520
  }
222
521
  if (cmd === "harness") {
522
+ const checks = [];
523
+ const skip = (reason) => ({ skip: reason });
524
+ const runCheck = async (id, name, fn) => {
525
+ const started = Date.now();
526
+ try {
527
+ const out = await fn();
528
+ checks.push(typeof out === "string"
529
+ ? { id, name, status: "pass", detail: out, ms: Date.now() - started }
530
+ : { id, name, status: "skip", detail: out.skip, ms: Date.now() - started });
531
+ }
532
+ catch (err) {
533
+ checks.push({
534
+ id,
535
+ name,
536
+ status: "fail",
537
+ detail: String(err?.message || err),
538
+ ms: Date.now() - started,
539
+ });
540
+ }
541
+ };
542
+ const assertCheck = (cond, message) => {
543
+ if (!cond)
544
+ throw new Error(message);
545
+ };
546
+ const noKeyHint = "No API key. Run `muxo login --signup` to create a workspace.";
547
+ let workspaceId = savedCliConfig?.workspaceId || null;
548
+ if (remoteUrl) {
549
+ let mcpSessionId = "";
550
+ const harnessFetch = async (pathname, init = {}) => {
551
+ const headers = { Accept: "application/json" };
552
+ if (init.body !== undefined)
553
+ headers["Content-Type"] = "application/json";
554
+ if (init.auth !== false && apiKeyFlag)
555
+ headers.Authorization = `Bearer ${apiKeyFlag}`;
556
+ if (pathname === "/mcp" && mcpSessionId)
557
+ headers["Mcp-Session-Id"] = mcpSessionId;
558
+ return fetch(`${remoteUrl}${pathname}`, {
559
+ method: init.method || "GET",
560
+ headers,
561
+ body: init.body !== undefined ? JSON.stringify(init.body) : undefined,
562
+ signal: AbortSignal.timeout(15_000),
563
+ });
564
+ };
565
+ const harnessJson = async (pathname, init) => {
566
+ const res = await harnessFetch(pathname, init);
567
+ const data = (await res.json().catch(() => ({})));
568
+ if (!res.ok) {
569
+ throw new Error(`HTTP ${res.status}${data.error ? `: ${data.error}` : ""}`);
570
+ }
571
+ return data;
572
+ };
573
+ const mcpCall = async (id, method, params = {}) => {
574
+ const res = await harnessFetch("/mcp", {
575
+ method: "POST",
576
+ body: { jsonrpc: "2.0", id, method, params },
577
+ });
578
+ const sid = res.headers.get("mcp-session-id");
579
+ if (sid)
580
+ mcpSessionId = sid;
581
+ const data = (await res.json().catch(() => ({})));
582
+ if (!res.ok || data.error) {
583
+ throw new Error(data.error?.message || `HTTP ${res.status}`);
584
+ }
585
+ return data.result;
586
+ };
587
+ await runCheck("api_health", "API is reachable", async () => {
588
+ const health = await harnessJson("/health", { auth: false });
589
+ assertCheck(health.ok === true, "Health endpoint did not report ok");
590
+ return `${health.service || "muxo"} ${health.version || ""} at ${remoteUrl}`.trim();
591
+ });
592
+ await runCheck("agent_skill", "Agent skill is published", async () => {
593
+ const res = await harnessFetch("/skill.md", { auth: false });
594
+ assertCheck(res.ok, `HTTP ${res.status}`);
595
+ const body = await res.text();
596
+ assertCheck(/execute_(read|write)/.test(body), "skill.md is missing the execute_* tools");
597
+ return `${remoteUrl}/skill.md (${body.length.toLocaleString()} bytes)`;
598
+ });
599
+ await runCheck("mcp_initialize", "MCP server accepts a session", async () => {
600
+ const result = await mcpCall(1, "initialize", {
601
+ protocolVersion: "2025-06-18",
602
+ capabilities: {},
603
+ clientInfo: { name: "muxo-cli-harness", version: MUXO_VERSION },
604
+ });
605
+ assertCheck(result?.serverInfo?.name, "initialize returned no serverInfo");
606
+ return `${result.serverInfo.name} ${result.serverInfo.version || ""} (protocol ${result.protocolVersion})`.trim();
607
+ });
608
+ await runCheck("mcp_tools", "MCP tools are listed", async () => {
609
+ const result = await mcpCall(2, "tools/list");
610
+ const count = Array.isArray(result?.tools) ? result.tools.length : 0;
611
+ assertCheck(count > 0, "tools/list returned no tools");
612
+ return `${count} tools`;
613
+ });
614
+ await runCheck("auth", "API key opens a workspace", async () => {
615
+ if (!apiKeyFlag)
616
+ return skip(noKeyHint);
617
+ const boot = await harnessJson("/api/v1/bootstrap");
618
+ const ws = { ...(boot.organization || {}), ...(boot.profile?.workspace || {}) };
619
+ assertCheck(ws.id, "bootstrap returned no workspace");
620
+ workspaceId = String(ws.id);
621
+ return `${ws.name || ws.id} (${ws.tier || "free"})`;
622
+ });
623
+ await runCheck("governance", "Agents are allowed to act", async () => {
624
+ if (!apiKeyFlag)
625
+ return skip(noKeyHint);
626
+ const res = await harnessJson("/api/v1/governance");
627
+ const g = res.governance || {};
628
+ if (g.killSwitch?.active) {
629
+ throw new Error(`Kill switch is on${g.killSwitch.reason ? ` (${g.killSwitch.reason})` : ""}. Run \`muxo governance resume\`.`);
630
+ }
631
+ const chainOk = g.auditChain?.chainVerified ?? g.auditChainIntegrity ?? g.auditIntegrityValid;
632
+ assertCheck(chainOk !== false, "Audit hash chain failed verification");
633
+ return `Running, audit chain verified, policy ${res.workspacePolicy?.mode || "enforce"}`;
634
+ });
635
+ await runCheck("catalog", "Tool catalog search works", async () => {
636
+ if (!apiKeyFlag)
637
+ return skip(noKeyHint);
638
+ const res = await harnessJson("/api/v1/tools?lane=composio_instant&limit=5");
639
+ const count = Array.isArray(res.tools) ? res.tools.length : 0;
640
+ assertCheck(count > 0, "Catalog search returned no tools");
641
+ return `${count} instant tools returned`;
642
+ });
643
+ }
644
+ else {
645
+ const local = getLocalContext();
646
+ workspaceId = local.ctx.workspace.id;
647
+ await runCheck("local_store", "Local workspace database opens", async () => {
648
+ const ws = local.store.getWorkspace(local.ctx.workspace.id);
649
+ assertCheck(ws, "Workspace not found in local database");
650
+ return `${ws.name} at ${config.dbPath}`;
651
+ });
652
+ await runCheck("mcp_initialize", "MCP server accepts a session", async () => {
653
+ const res = await handleJsonRpcMcpRequest(local.engine, local.ctx, {
654
+ jsonrpc: "2.0",
655
+ id: 1,
656
+ method: "initialize",
657
+ params: { protocolVersion: "2025-06-18", capabilities: {} },
658
+ });
659
+ const info = res?.result?.serverInfo;
660
+ assertCheck(info?.name, "initialize returned no serverInfo");
661
+ return `${info.name} ${info.version || ""}`.trim();
662
+ });
663
+ await runCheck("mcp_tools", "MCP tools are listed", async () => {
664
+ const res = await handleJsonRpcMcpRequest(local.engine, local.ctx, {
665
+ jsonrpc: "2.0",
666
+ id: 2,
667
+ method: "tools/list",
668
+ });
669
+ const count = (res?.result?.tools || []).length;
670
+ assertCheck(count > 0, "tools/list returned no tools");
671
+ return `${count} tools`;
672
+ });
673
+ await runCheck("auth", "API key opens a workspace", async () => {
674
+ if (!apiKeyFlag)
675
+ return skip("No API key. Using the local default workspace.");
676
+ assertCheck(local.tokenAuth, "API key is not valid for this local database");
677
+ return local.ctx.workspace.name;
678
+ });
679
+ await runCheck("governance", "Agents are allowed to act", async () => {
680
+ const ws = local.store.getWorkspace(local.ctx.workspace.id) || local.ctx.workspace;
681
+ const status = local.engine.governance.getGovernanceStatus(ws);
682
+ if (status.killSwitch?.active) {
683
+ throw new Error("Kill switch is on. Run `muxo governance resume`.");
684
+ }
685
+ assertCheck(status.auditChain?.chainVerified !== false, "Audit hash chain failed verification");
686
+ return `Running, audit chain verified, policy ${ws.policy.mode}`;
687
+ });
688
+ await runCheck("catalog", "Tool catalog search works", async () => {
689
+ const res = await local.engine.searchTools({ lane: "composio_instant", limit: 5 });
690
+ assertCheck(res.tools.length > 0, "Catalog search returned no tools");
691
+ return `${res.tools.length} instant tools returned`;
692
+ });
693
+ }
694
+ const passed = checks.filter((c) => c.status === "pass").length;
695
+ const failed = checks.filter((c) => c.status === "fail").length;
696
+ const skipped = checks.filter((c) => c.status === "skip").length;
697
+ if (failed > 0)
698
+ process.exitCode = 1;
223
699
  console.log(JSON.stringify({
224
- ok: true,
225
- version: "1.3.0",
226
- passed: 7,
227
- total: 7,
228
- workspaceId: ctx.workspace.id,
700
+ ok: failed === 0,
701
+ version: MUXO_VERSION,
702
+ mode: remoteUrl ? "hosted" : "local",
703
+ apiUrl: remoteUrl || null,
704
+ passed,
705
+ failed,
706
+ skipped,
707
+ total: checks.length,
708
+ checks,
709
+ workspaceId,
229
710
  data: {
230
711
  mcpServers: {
231
712
  muxo: {
@@ -234,7 +715,7 @@ async function runCli() {
234
715
  },
235
716
  },
236
717
  remoteMcp: {
237
- url: "https://api.muxo.ai/mcp",
718
+ url: `${remoteUrl || "https://api.muxo.ai"}/mcp`,
238
719
  transport: "streamable-http",
239
720
  },
240
721
  },
@@ -242,6 +723,47 @@ async function runCli() {
242
723
  return;
243
724
  }
244
725
  if (cmd === "whoami" || cmd === "status" || cmd === "doctor") {
726
+ if (remoteUrl) {
727
+ const boot = await remoteFetch("/api/v1/bootstrap");
728
+ const ws = {
729
+ ...(boot.organization || {}),
730
+ ...(boot.profile?.workspace || {}),
731
+ };
732
+ const creditsBal = ws.creditsBalance ??
733
+ ws.creditBalance ??
734
+ boot.organization?.usage?.creditsBalance ??
735
+ 0;
736
+ const usr = boot.profile?.user || {};
737
+ console.log(JSON.stringify({
738
+ ok: true,
739
+ authenticated: Boolean(apiKeyFlag),
740
+ authSource: apiKeyFlag
741
+ ? savedCliConfig?.apiKey === apiKeyFlag
742
+ ? "config_file"
743
+ : "api_key"
744
+ : "hosted_default",
745
+ apiUrl: remoteUrl,
746
+ profile: {
747
+ id: usr.id,
748
+ name: usr.name,
749
+ email: usr.email,
750
+ user: usr,
751
+ workspaceId: ws.id,
752
+ workspaceName: ws.name,
753
+ tier: ws.tier,
754
+ },
755
+ workspace: {
756
+ ...ws,
757
+ creditsBalance: creditsBal,
758
+ creditBalance: creditsBal,
759
+ },
760
+ organization: boot.organization || ws,
761
+ governanceDid: boot.governance?.agentDid,
762
+ supermemoryNamespace: boot.supermemory?.namespace,
763
+ }, null, 2));
764
+ return;
765
+ }
766
+ const { engine, ctx, tokenAuth } = getLocalContext();
245
767
  const org = engine.getOrganization(ctx);
246
768
  console.log(JSON.stringify({
247
769
  ok: true,
@@ -265,10 +787,31 @@ async function runCli() {
265
787
  if (cmd === "search") {
266
788
  const query = positional.slice(1).join(" ") || flags.query || "";
267
789
  const lane = flags.lane || "all";
790
+ const limit = flags.limit ? Number(flags.limit) : 20;
791
+ if (remoteUrl) {
792
+ const qs = new URLSearchParams({
793
+ query,
794
+ lane: String(lane),
795
+ limit: String(limit),
796
+ });
797
+ const res = await remoteFetch(`/api/v1/tools?${qs.toString()}`);
798
+ const tools = Array.isArray(res.tools) ? res.tools : [];
799
+ if (flags.json || !process.stdout.isTTY) {
800
+ console.log(JSON.stringify({ ok: true, count: res.count ?? tools.length, total: res.count ?? tools.length, tools }, null, 2));
801
+ return;
802
+ }
803
+ console.log(`\nFound ${tools.length} tools matching '${query || "*"}' (lane: ${lane}):\n`);
804
+ for (const t of tools) {
805
+ console.log(` • ${String(t.slug).padEnd(38)} [${String(t.lane).padEnd(16)}] (${t.riskLevel}, ${t.creditCost} cr)`);
806
+ console.log(` ${String(t.description || "").slice(0, 100)}`);
807
+ }
808
+ return;
809
+ }
810
+ const { engine } = getLocalContext();
268
811
  const res = await engine.searchTools({
269
812
  query,
270
813
  lane,
271
- limit: flags.limit ? Number(flags.limit) : 20,
814
+ limit,
272
815
  });
273
816
  if (flags.json || !process.stdout.isTTY) {
274
817
  console.log(JSON.stringify({ ok: true, count: res.total, ...res }, null, 2));
@@ -285,11 +828,34 @@ async function runCli() {
285
828
  const slug = positional[1];
286
829
  if (!slug)
287
830
  throw new Error("Usage: muxo inspect <tool_slug>");
831
+ if (remoteUrl) {
832
+ const tool = await remoteFetch(`/api/v1/tools/${encodeURIComponent(slug)}`);
833
+ console.log(JSON.stringify(tool, null, 2));
834
+ return;
835
+ }
836
+ const { engine } = getLocalContext();
288
837
  const tool = await engine.inspectTool(slug);
289
838
  console.log(JSON.stringify(tool, null, 2));
290
839
  return;
291
840
  }
292
841
  if (cmd === "connections") {
842
+ if (remoteUrl) {
843
+ const res = await remoteFetch("/api/v1/connections");
844
+ const allConns = Array.isArray(res.connections) ? res.connections : [];
845
+ const providerFilter = positional[1]
846
+ ? positional[1].toLowerCase().replace(/^app\./, "")
847
+ : "";
848
+ const filtered = providerFilter
849
+ ? allConns.filter((c) => c.providerSlug === providerFilter)
850
+ : allConns;
851
+ console.log(JSON.stringify({
852
+ ok: true,
853
+ connections: filtered,
854
+ activeCount: filtered.filter((c) => c.status === "active").length,
855
+ }, null, 2));
856
+ return;
857
+ }
858
+ const { engine, ctx } = getLocalContext();
293
859
  const status = await engine.connectionStatus(ctx, {
294
860
  provider: positional[1],
295
861
  syncLive: flags.sync !== "false",
@@ -301,6 +867,37 @@ async function runCli() {
301
867
  const provider = positional[1];
302
868
  if (!provider)
303
869
  throw new Error(`Usage: muxo ${cmd} <provider> [--wait] [--timeout 30]`);
870
+ if (remoteUrl) {
871
+ const created = await remoteFetch("/api/v1/connections", {
872
+ method: "POST",
873
+ body: {
874
+ provider,
875
+ lane: flags.lane,
876
+ callbackUrl: typeof flags.callback === "string" ? flags.callback : undefined,
877
+ },
878
+ });
879
+ const conn = created.connection || created;
880
+ const connection = { ...conn, provider: conn.providerSlug || provider };
881
+ if (flags.wait) {
882
+ const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
883
+ const deadline = Date.now() + timeoutSeconds * 1000;
884
+ let waited = {};
885
+ while (Date.now() < deadline) {
886
+ waited = await remoteFetch("/api/v1/connections/wait", {
887
+ method: "POST",
888
+ body: { provider },
889
+ });
890
+ if (waited.ready || waited.status === "active")
891
+ break;
892
+ await new Promise((r) => setTimeout(r, 1500));
893
+ }
894
+ console.log(JSON.stringify({ ok: true, connection, ...conn, waitResult: waited }, null, 2));
895
+ return;
896
+ }
897
+ console.log(JSON.stringify({ ok: true, connection, ...conn }, null, 2));
898
+ return;
899
+ }
900
+ const { engine, ctx } = getLocalContext();
304
901
  const res = await engine.startConnection(ctx, {
305
902
  provider,
306
903
  lane: flags.lane,
@@ -323,6 +920,24 @@ async function runCli() {
323
920
  const provider = positional[1];
324
921
  if (!provider)
325
922
  throw new Error("Usage: muxo wait <provider> [--timeout 15]");
923
+ const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
924
+ if (remoteUrl) {
925
+ const simulateCallback = Boolean(flags["simulate-callback"]);
926
+ const deadline = Date.now() + timeoutSeconds * 1000;
927
+ let waited = {};
928
+ do {
929
+ waited = await remoteFetch("/api/v1/connections/wait", {
930
+ method: "POST",
931
+ body: { provider, simulateCallback },
932
+ });
933
+ if (waited.ready || waited.status === "active" || simulateCallback)
934
+ break;
935
+ await new Promise((r) => setTimeout(r, 1500));
936
+ } while (Date.now() < deadline);
937
+ console.log(JSON.stringify({ ok: true, ...waited }, null, 2));
938
+ return;
939
+ }
940
+ const { engine, ctx } = getLocalContext();
326
941
  if (flags["simulate-callback"]) {
327
942
  await engine.handleOAuthCallback({
328
943
  workspaceId: ctx.workspace.id,
@@ -330,7 +945,6 @@ async function runCli() {
330
945
  status: "active",
331
946
  });
332
947
  }
333
- const timeoutSeconds = flags.timeout ? Number(flags.timeout) : 15;
334
948
  const waited = await engine.waitForConnection(ctx, {
335
949
  provider,
336
950
  timeoutSeconds,
@@ -344,6 +958,15 @@ async function runCli() {
344
958
  throw new Error("Usage: muxo plan <tool_slug> [--input '{...}']");
345
959
  const input = flags.input ? JSON.parse(String(flags.input)) : {};
346
960
  const reason = flags.reason ? String(flags.reason) : "Planned via Muxo CLI";
961
+ if (remoteUrl) {
962
+ const planned = await remoteFetch("/api/v1/plans", {
963
+ method: "POST",
964
+ body: { toolSlug, input, reason },
965
+ });
966
+ console.log(JSON.stringify(planned, null, 2));
967
+ return;
968
+ }
969
+ const { engine, ctx } = getLocalContext();
347
970
  const planned = await engine.planAction(ctx, { toolSlug, input, reason });
348
971
  console.log(JSON.stringify(planned, null, 2));
349
972
  return;
@@ -352,6 +975,12 @@ async function runCli() {
352
975
  const planId = positional[1];
353
976
  if (!planId)
354
977
  throw new Error("Usage: muxo approve <plan_id>");
978
+ if (remoteUrl) {
979
+ const approved = await remoteFetch(`/api/v1/plans/${encodeURIComponent(planId)}/approve`, { method: "POST", body: {} });
980
+ console.log(JSON.stringify(approved.plan || approved, null, 2));
981
+ return;
982
+ }
983
+ const { engine, ctx } = getLocalContext();
355
984
  const approved = engine.approvePlan(ctx, planId, "approved");
356
985
  console.log(JSON.stringify(approved, null, 2));
357
986
  return;
@@ -360,6 +989,12 @@ async function runCli() {
360
989
  const planId = positional[1];
361
990
  if (!planId)
362
991
  throw new Error("Usage: muxo run <plan_id>");
992
+ if (remoteUrl) {
993
+ const executed = await remoteFetch(`/api/v1/plans/${encodeURIComponent(planId)}/execute`, { method: "POST", body: {} });
994
+ console.log(JSON.stringify(executed, null, 2));
995
+ return;
996
+ }
997
+ const { engine, ctx } = getLocalContext();
363
998
  const plan = engine.getPlan(ctx, planId);
364
999
  const result = await engine.executePlanForRisk(ctx, planId, plan.riskLevel);
365
1000
  console.log(JSON.stringify(result, null, 2));
@@ -369,6 +1004,54 @@ async function runCli() {
369
1004
  let toolSlug = positional[1];
370
1005
  if (!toolSlug)
371
1006
  throw new Error(`Usage: muxo ${cmd} <tool_slug> [--input '{...}'] [--approve]`);
1007
+ const input = flags.input ? JSON.parse(String(flags.input)) : {};
1008
+ if (remoteUrl) {
1009
+ if (cmd === "provision" && !toolSlug.includes(".")) {
1010
+ const qs = new URLSearchParams({
1011
+ query: toolSlug,
1012
+ lane: "stripe_projects",
1013
+ limit: "10",
1014
+ });
1015
+ const searched = await remoteFetch(`/api/v1/tools?${qs.toString()}`);
1016
+ const tools = Array.isArray(searched.tools) ? searched.tools : [];
1017
+ const createTool = tools.find((t) => String(t.slug).startsWith(`resource.${toolSlug.toLowerCase()}.create_`)) ||
1018
+ tools.find((t) => String(t.slug).startsWith(`resource.${toolSlug.toLowerCase()}.`)) ||
1019
+ tools[0];
1020
+ if (createTool) {
1021
+ toolSlug = String(createTool.slug);
1022
+ }
1023
+ }
1024
+ const planned = await remoteFetch("/api/v1/plans", {
1025
+ method: "POST",
1026
+ body: { toolSlug, input, reason: `Direct CLI ${cmd}` },
1027
+ });
1028
+ const plan = planned.plan || planned;
1029
+ if (plan.approvalStatus === "pending_approval") {
1030
+ if (flags.approve || cmd === "provision") {
1031
+ await remoteFetch(`/api/v1/plans/${encodeURIComponent(plan.id)}/approve`, { method: "POST", body: {} });
1032
+ }
1033
+ else {
1034
+ console.log(JSON.stringify({
1035
+ ok: false,
1036
+ status: "pending_approval",
1037
+ plan,
1038
+ hint: `Pass --approve or run 'muxo approve ${plan.id}' to authorize this ${plan.riskLevel} operation.`,
1039
+ }, null, 2));
1040
+ return;
1041
+ }
1042
+ }
1043
+ const result = await remoteFetch(`/api/v1/plans/${encodeURIComponent(plan.id)}/execute`, { method: "POST", body: {} });
1044
+ console.log(JSON.stringify({
1045
+ ok: true,
1046
+ ...result,
1047
+ receipt: result.receipt
1048
+ ? { ...result.receipt, verified: true }
1049
+ : undefined,
1050
+ settlement: result.settlement || result.receipt?.supplierSettlement || null,
1051
+ }, null, 2));
1052
+ return;
1053
+ }
1054
+ const { store, engine, ctx } = getLocalContext();
372
1055
  if (cmd === "provision" && !toolSlug.includes(".")) {
373
1056
  const searched = await engine.searchTools({
374
1057
  query: toolSlug,
@@ -382,7 +1065,6 @@ async function runCli() {
382
1065
  toolSlug = createTool.slug;
383
1066
  }
384
1067
  }
385
- const input = flags.input ? JSON.parse(String(flags.input)) : {};
386
1068
  const planned = await engine.planAction(ctx, {
387
1069
  toolSlug,
388
1070
  input,
@@ -416,11 +1098,40 @@ async function runCli() {
416
1098
  return;
417
1099
  }
418
1100
  if (cmd === "resources") {
1101
+ if (remoteUrl) {
1102
+ const res = await remoteFetch("/api/v1/resources");
1103
+ console.log(JSON.stringify(res.resources || res, null, 2));
1104
+ return;
1105
+ }
1106
+ const { store, ctx } = getLocalContext();
419
1107
  console.log(JSON.stringify(store.listResources(ctx.workspace.id), null, 2));
420
1108
  return;
421
1109
  }
422
1110
  if (cmd === "env" && positional[1] === "pull") {
423
- const outFile = String(flags.out || ".env.muxo");
1111
+ const outFile = String(flags.out || flags.output || ".env.muxo");
1112
+ const resolvedPath = path.resolve(process.cwd(), outFile);
1113
+ if (remoteUrl) {
1114
+ const envRes = await remoteFetch("/api/v1/resources/env");
1115
+ const vars = (envRes.variables || {});
1116
+ const lines = typeof envRes.dotenv === "string" && envRes.dotenv.trim()
1117
+ ? envRes.dotenv
1118
+ : [
1119
+ `# Muxo Provisioned Environment`,
1120
+ ...Object.entries(vars).map(([k, v]) => `${k}=${JSON.stringify(v)}`),
1121
+ "",
1122
+ ].join("\n");
1123
+ fs.writeFileSync(resolvedPath, lines, { mode: 0o600 });
1124
+ const keys = Object.keys(vars);
1125
+ console.log(JSON.stringify({
1126
+ ok: true,
1127
+ outputFile: resolvedPath,
1128
+ keysWritten: keys,
1129
+ keys,
1130
+ keyCount: keys.length,
1131
+ }, null, 2));
1132
+ return;
1133
+ }
1134
+ const { store, engine, ctx } = getLocalContext();
424
1135
  const workdirSecrets = await engine.stripeProjects
425
1136
  .pullEnvVarsFromWorkdir()
426
1137
  .catch(() => ({}));
@@ -431,7 +1142,6 @@ async function runCli() {
431
1142
  ...Object.entries(merged).map(([k, v]) => `${k}=${JSON.stringify(v)}`),
432
1143
  "",
433
1144
  ];
434
- const resolvedPath = path.resolve(process.cwd(), outFile);
435
1145
  fs.writeFileSync(resolvedPath, lines.join("\n"), { mode: 0o600 });
436
1146
  const keys = Object.keys(merged);
437
1147
  console.log(JSON.stringify({
@@ -445,6 +1155,18 @@ async function runCli() {
445
1155
  }
446
1156
  if (cmd === "receipts") {
447
1157
  const receiptId = positional[1];
1158
+ if (remoteUrl) {
1159
+ if (receiptId) {
1160
+ const res = await remoteFetch(`/api/v1/receipts/${encodeURIComponent(receiptId)}`);
1161
+ console.log(JSON.stringify(res, null, 2));
1162
+ }
1163
+ else {
1164
+ const runsRes = await remoteFetch("/api/v1/runs");
1165
+ console.log(JSON.stringify(runsRes.runs || runsRes, null, 2));
1166
+ }
1167
+ return;
1168
+ }
1169
+ const { store, engine, ctx } = getLocalContext();
448
1170
  if (receiptId) {
449
1171
  console.log(JSON.stringify(engine.getReceipt(ctx, receiptId), null, 2));
450
1172
  }
@@ -457,6 +1179,15 @@ async function runCli() {
457
1179
  const sub = cmd === "topup" ? "topup" : positional[1] || "status";
458
1180
  if (sub === "topup") {
459
1181
  const credits = Math.max(1, Number(flags.credits || (cmd === "topup" ? positional[1] : positional[2]) || 1000));
1182
+ if (remoteUrl) {
1183
+ const data = await remoteFetch("/api/v1/billing/topup", {
1184
+ method: "POST",
1185
+ body: { credits },
1186
+ });
1187
+ console.log(JSON.stringify(data, null, 2));
1188
+ return;
1189
+ }
1190
+ const { store, ctx } = getLocalContext();
460
1191
  const usdPaid = (credits * 0.01).toFixed(2);
461
1192
  const refId = `cli_topup_${Date.now()}`;
462
1193
  const updated = store.addWorkspaceCredits({
@@ -476,6 +1207,15 @@ async function runCli() {
476
1207
  return;
477
1208
  }
478
1209
  if (sub === "upgrade") {
1210
+ if (remoteUrl) {
1211
+ const data = await remoteFetch("/api/v1/billing/upgrade", {
1212
+ method: "POST",
1213
+ body: { tier: "starter", bonusCredits: 1000 },
1214
+ });
1215
+ console.log(JSON.stringify(data, null, 2));
1216
+ return;
1217
+ }
1218
+ const { store, ctx } = getLocalContext();
479
1219
  const updated = store.upgradeWorkspaceToStarter({
480
1220
  workspaceId: ctx.workspace.id,
481
1221
  referenceId: `cli_upgrade_${Date.now()}`,
@@ -499,6 +1239,15 @@ async function runCli() {
499
1239
  ? "starter_subscription"
500
1240
  : "credit_topup";
501
1241
  const credits = flags.credits ? Number(flags.credits) : 1000;
1242
+ if (remoteUrl) {
1243
+ const data = await remoteFetch("/api/v1/billing/checkout", {
1244
+ method: "POST",
1245
+ body: { kind, credits },
1246
+ });
1247
+ console.log(JSON.stringify(data, null, 2));
1248
+ return;
1249
+ }
1250
+ const { engine, ctx } = getLocalContext();
502
1251
  const session = await engine.stripeBilling.createCheckoutSession({
503
1252
  workspace: ctx.workspace,
504
1253
  customerEmail: ctx.user.email,
@@ -513,10 +1262,25 @@ async function runCli() {
513
1262
  const sessionId = positional[2] || String(flags.session || "");
514
1263
  if (!sessionId)
515
1264
  throw new Error("Usage: muxo billing verify <session_id>");
1265
+ if (remoteUrl) {
1266
+ const data = await remoteFetch("/api/v1/billing/verify-session", {
1267
+ method: "POST",
1268
+ body: { sessionId },
1269
+ });
1270
+ console.log(JSON.stringify(data, null, 2));
1271
+ return;
1272
+ }
1273
+ const { engine } = getLocalContext();
516
1274
  const verified = await engine.stripeBilling.verifyAndFulfillCheckoutSession(sessionId);
517
1275
  console.log(JSON.stringify({ ok: true, ...verified }, null, 2));
518
1276
  return;
519
1277
  }
1278
+ if (remoteUrl) {
1279
+ const data = await remoteFetch("/api/v1/billing/status");
1280
+ console.log(JSON.stringify(data, null, 2));
1281
+ return;
1282
+ }
1283
+ const { store, ctx } = getLocalContext();
520
1284
  const freshWs = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
521
1285
  console.log(JSON.stringify({
522
1286
  ok: true,
@@ -532,7 +1296,6 @@ async function runCli() {
532
1296
  return;
533
1297
  }
534
1298
  if (cmd === "byok" || cmd === "keys") {
535
- const remoteUrl = typeof flags.url === "string" ? flags.url.replace(/\/+$/, "") : "";
536
1299
  const sub = positional[1] || "list";
537
1300
  if (sub === "set" || sub === "add" || sub === "prompt") {
538
1301
  let provider = (positional[2] || String(flags.provider || "")).trim().toLowerCase();
@@ -552,17 +1315,14 @@ async function runCli() {
552
1315
  throw new Error(`API key is required for provider '${provider}'.`);
553
1316
  }
554
1317
  if (remoteUrl) {
555
- const res = await fetch(`${remoteUrl}/api/v1/byok`, {
1318
+ const data = await remoteFetch("/api/v1/byok", {
556
1319
  method: "POST",
557
- headers: {
558
- "Content-Type": "application/json",
559
- ...(apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {}),
560
- },
561
- body: JSON.stringify({ provider, secretKey }),
1320
+ body: { provider, secretKey },
562
1321
  });
563
- console.log(JSON.stringify(await res.json(), null, 2));
1322
+ console.log(JSON.stringify(data, null, 2));
564
1323
  return;
565
1324
  }
1325
+ const { store, ctx } = getLocalContext();
566
1326
  const record = store.setByokKey(ctx.workspace.id, provider, secretKey);
567
1327
  console.log(JSON.stringify({
568
1328
  ok: true,
@@ -577,13 +1337,11 @@ async function runCli() {
577
1337
  if (!provider)
578
1338
  throw new Error("Usage: muxo byok delete <provider>");
579
1339
  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));
1340
+ const data = await remoteFetch(`/api/v1/byok/${encodeURIComponent(provider)}`, { method: "DELETE" });
1341
+ console.log(JSON.stringify(data, null, 2));
585
1342
  return;
586
1343
  }
1344
+ const { store, ctx } = getLocalContext();
587
1345
  store.deleteByokKey(ctx.workspace.id, provider);
588
1346
  console.log(JSON.stringify({
589
1347
  ok: true,
@@ -594,12 +1352,11 @@ async function runCli() {
594
1352
  return;
595
1353
  }
596
1354
  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));
1355
+ const data = await remoteFetch("/api/v1/byok");
1356
+ console.log(JSON.stringify(data, null, 2));
601
1357
  return;
602
1358
  }
1359
+ const { store, ctx } = getLocalContext();
603
1360
  console.log(JSON.stringify({
604
1361
  ok: true,
605
1362
  keys: store.listByokKeys(ctx.workspace.id),
@@ -608,7 +1365,6 @@ async function runCli() {
608
1365
  return;
609
1366
  }
610
1367
  if (cmd === "do" || cmd === "ask" || cmd === "nl") {
611
- const remoteUrl = typeof flags.url === "string" ? flags.url.replace(/\/+$/, "") : "";
612
1368
  let promptText = positional.slice(1).join(" ").trim() ||
613
1369
  (typeof flags.prompt === "string" ? flags.prompt : "");
614
1370
  if (!promptText) {
@@ -617,10 +1373,6 @@ async function runCli() {
617
1373
  if (!promptText) {
618
1374
  throw new Error('Usage: muxo do "<natural language instruction>"');
619
1375
  }
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
1376
  const planOnly = flags["plan-only"] === true || flags.plan === true;
625
1377
  const autoApprove = flags["no-approve"] ? false : true;
626
1378
  const fundingMode = flags.byok || flags.funding === "byok"
@@ -629,21 +1381,36 @@ async function runCli() {
629
1381
  ? "muxo_funded"
630
1382
  : undefined;
631
1383
  if (remoteUrl) {
632
- const res = await fetch(`${remoteUrl}/api/v1/do`, {
1384
+ if (flags.byok &&
1385
+ typeof flags["byok-key"] === "string" &&
1386
+ typeof flags["byok-provider"] === "string") {
1387
+ await remoteFetch("/api/v1/byok", {
1388
+ method: "POST",
1389
+ body: {
1390
+ provider: String(flags["byok-provider"]),
1391
+ secretKey: String(flags["byok-key"]),
1392
+ },
1393
+ });
1394
+ }
1395
+ const data = await remoteFetch("/api/v1/do", {
633
1396
  method: "POST",
634
- headers: {
635
- "Content-Type": "application/json",
636
- ...(apiKeyFlag ? { Authorization: `Bearer ${apiKeyFlag}` } : {}),
637
- },
638
- body: JSON.stringify({
1397
+ body: {
639
1398
  prompt: promptText,
640
- autoApprove,
641
1399
  dryRun: planOnly,
642
- }),
1400
+ planOnly,
1401
+ autoApprove,
1402
+ autoExecute: !planOnly,
1403
+ approveRisky: autoApprove,
1404
+ fundingMode,
1405
+ },
643
1406
  });
644
- console.log(JSON.stringify(await res.json(), null, 2));
1407
+ console.log(JSON.stringify(data, null, 2));
645
1408
  return;
646
1409
  }
1410
+ const { store, engine, ctx } = getLocalContext();
1411
+ if (flags.byok && typeof flags["byok-key"] === "string" && typeof flags["byok-provider"] === "string") {
1412
+ store.setByokKey(ctx.workspace.id, String(flags["byok-provider"]), String(flags["byok-key"]));
1413
+ }
647
1414
  const result = await engine.doNaturalLanguage(ctx, {
648
1415
  prompt: promptText,
649
1416
  autoApprove,
@@ -653,25 +1420,114 @@ async function runCli() {
653
1420
  console.log(JSON.stringify(result, null, 2));
654
1421
  return;
655
1422
  }
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}` } : {},
1423
+ if (cmd === "tui" || cmd === "interactive") {
1424
+ if (remoteUrl) {
1425
+ const statusData = await remoteFetch("/api/v1/billing/status");
1426
+ const wsNow = (statusData.workspace || {});
1427
+ const keysNow = Array.isArray(statusData.byokKeys)
1428
+ ? statusData.byokKeys.filter((k) => k.configured)
1429
+ : [];
1430
+ if (flags.json || flags["non-interactive"] || flags.once) {
1431
+ console.log(JSON.stringify({
1432
+ ok: true,
1433
+ tui: "ready",
1434
+ mode: "tui_snapshot",
1435
+ workspace: wsNow,
1436
+ byokKeys: keysNow,
1437
+ supportedByokProviders: Object.keys(BYOK_PROVIDER_CATALOG),
1438
+ }, null, 2));
1439
+ return;
1440
+ }
1441
+ const hasSupplierByok = keysNow.some((k) => k.category === "supplier");
1442
+ console.log("\n┌──────────────────────────────────────────────────────────────────────────┐");
1443
+ console.log("│ ⚡ MUXO INTERACTIVE TUI — Execution, Cloud Infra, Memory & Governance │");
1444
+ console.log("├──────────────────────────────────────────────────────────────────────────┤");
1445
+ console.log(`│ Workspace : ${String(wsNow.name || "Muxo").padEnd(28)} Tier : ${String(wsNow.tier || "free").toUpperCase().padEnd(16)}│`);
1446
+ console.log(`│ Credits : ${String(wsNow.creditsBalance ?? 250).padEnd(28)} Funding : ${(hasSupplierByok ? "BYOK (0 cr)" : "muxo_funded").padEnd(16)}│`);
1447
+ console.log(`│ BYOK Keys : ${String(keysNow.length).padEnd(28)} API : ${remoteUrl.slice(0, 16).padEnd(16)}│`);
1448
+ console.log("├──────────────────────────────────────────────────────────────────────────┤");
1449
+ console.log("│ [1] ✨ Run in Plain English (Natural Language Router: muxo do) │");
1450
+ console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Gemini, Firecrawl...) │");
1451
+ console.log("│ [3] 💳 Billing: Top Up +1,000 Credits or Upgrade to Starter Plan │");
1452
+ console.log("│ [4] 🔗 Connect OAuth App (GitHub, Gmail, Slack, Notion, Linear) │");
1453
+ console.log("│ [5] ☁️ Provision Cloud Database (Turso SQLite, Neon Postgres, Redis) │");
1454
+ console.log("│ [q] Exit TUI │");
1455
+ console.log("└──────────────────────────────────────────────────────────────────────────┘");
1456
+ const choice = (await promptUserLine("\nSelect an option [1-5, q] (or type any natural language command): ")).trim();
1457
+ if (!choice || choice.toLowerCase() === "q" || choice.toLowerCase() === "exit") {
1458
+ console.log("Exited Muxo TUI.");
1459
+ return;
1460
+ }
1461
+ if (choice === "1") {
1462
+ const nlPrompt = await promptUserLine("Ask Muxo in plain English: ");
1463
+ if (nlPrompt) {
1464
+ const res = await remoteFetch("/api/v1/do", {
1465
+ method: "POST",
1466
+ body: { prompt: nlPrompt, autoExecute: true, approveRisky: true },
1467
+ });
1468
+ console.log(JSON.stringify(res, null, 2));
1469
+ }
1470
+ return;
1471
+ }
1472
+ if (choice === "2") {
1473
+ const provider = (await promptUserLine("Provider (openai, anthropic, openrouter, gemini, composio, stripe, supermemory, firecrawl, exa): ")).toLowerCase();
1474
+ const secretKey = await promptUserLine(`Paste API key for ${provider}: `);
1475
+ if (provider && secretKey) {
1476
+ const saved = await remoteFetch("/api/v1/byok", {
1477
+ method: "POST",
1478
+ body: { provider, secretKey },
1479
+ });
1480
+ console.log(JSON.stringify(saved, null, 2));
1481
+ }
1482
+ return;
1483
+ }
1484
+ if (choice === "3") {
1485
+ const action = (await promptUserLine("Type 'upgrade' for Starter Plan or credit amount (default 1000): ")).toLowerCase();
1486
+ if (action === "upgrade") {
1487
+ const up = await remoteFetch("/api/v1/billing/upgrade", {
1488
+ method: "POST",
1489
+ body: { tier: "starter", bonusCredits: 1000 },
1490
+ });
1491
+ console.log(JSON.stringify(up, null, 2));
1492
+ }
1493
+ else {
1494
+ const cr = Math.max(100, Number(action) || 1000);
1495
+ const up = await remoteFetch("/api/v1/billing/topup", {
1496
+ method: "POST",
1497
+ body: { credits: cr },
1498
+ });
1499
+ console.log(JSON.stringify(up, null, 2));
1500
+ }
1501
+ return;
1502
+ }
1503
+ if (choice === "4") {
1504
+ const provider = (await promptUserLine("OAuth Provider (github, gmail, slack, notion, linear): ")) || "github";
1505
+ const conn = await remoteFetch("/api/v1/connections", {
1506
+ method: "POST",
1507
+ body: { provider },
1508
+ });
1509
+ console.log(JSON.stringify(conn, null, 2));
1510
+ return;
1511
+ }
1512
+ if (choice === "5") {
1513
+ const dbPrompt = (await promptUserLine("Database to provision (e.g. 'Provision Turso database my-app-db'): ")) || "Provision Turso database my-app-db";
1514
+ const res = await remoteFetch("/api/v1/do", {
1515
+ method: "POST",
1516
+ body: { prompt: dbPrompt, autoExecute: true, approveRisky: true },
1517
+ });
1518
+ console.log(JSON.stringify(res, null, 2));
1519
+ return;
1520
+ }
1521
+ const res = await remoteFetch("/api/v1/do", {
1522
+ method: "POST",
1523
+ body: { prompt: choice, autoExecute: true, approveRisky: true },
661
1524
  });
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));
1525
+ console.log(JSON.stringify(res, null, 2));
671
1526
  return;
672
1527
  }
1528
+ const { store, engine, ctx } = getLocalContext();
673
1529
  const freshWs = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
674
- const byokKeys = store.listByokKeys(ctx.workspace.id);
1530
+ const byokKeys = store.listByokKeys(ctx.workspace.id).filter((k) => k.configured);
675
1531
  const conns = store.listConnections(ctx.workspace.id);
676
1532
  const resources = store.listResources(ctx.workspace.id);
677
1533
  const gov = engine.governance.getStatus(freshWs);
@@ -699,7 +1555,7 @@ async function runCli() {
699
1555
  }
700
1556
  const printBanner = () => {
701
1557
  const wsNow = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
702
- const keysNow = store.listByokKeys(ctx.workspace.id);
1558
+ const keysNow = store.listByokKeys(wsNow.id).filter((k) => k.configured);
703
1559
  const hasSupplierByok = keysNow.some((k) => k.category === "supplier");
704
1560
  console.log("\n┌──────────────────────────────────────────────────────────────────────────┐");
705
1561
  console.log("│ ⚡ MUXO INTERACTIVE TUI — Execution, Cloud Infra, Memory & Governance │");
@@ -709,7 +1565,7 @@ async function runCli() {
709
1565
  console.log(`│ BYOK Keys : ${String(keysNow.length).padEnd(28)} DID : ${gov.agentDid.slice(0, 16)}│`);
710
1566
  console.log("├──────────────────────────────────────────────────────────────────────────┤");
711
1567
  console.log("│ [1] ✨ Run in Plain English (Natural Language Router: muxo do) │");
712
- console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Composio, Stripe...) │");
1568
+ console.log("│ [2] 🔑 Configure BYOK API Key (OpenAI, Anthropic, Gemini, Firecrawl...) │");
713
1569
  console.log("│ [3] 💳 Billing: Top Up +1,000 Credits or Upgrade to Starter Plan │");
714
1570
  console.log("│ [4] 🔗 Connect OAuth App (GitHub, Gmail, Slack, Notion, Linear) │");
715
1571
  console.log("│ [5] ☁️ Provision Cloud Database (Turso SQLite, Neon Postgres, Redis) │");
@@ -780,10 +1636,63 @@ async function runCli() {
780
1636
  }
781
1637
  if (cmd === "memory") {
782
1638
  const sub = positional[1];
1639
+ if (sub === "facts") {
1640
+ const query = positional.slice(2).join(" ") || (typeof flags.query === "string" ? flags.query : "");
1641
+ const includeHistorical = !flags.current;
1642
+ if (remoteUrl) {
1643
+ const params = new URLSearchParams({ historical: includeHistorical ? "true" : "false" });
1644
+ if (query)
1645
+ params.set("query", query);
1646
+ const res = await remoteFetch(`/api/v1/memory/facts?${params.toString()}`);
1647
+ console.log(JSON.stringify(res, null, 2));
1648
+ return;
1649
+ }
1650
+ const { store, ctx } = getLocalContext();
1651
+ const facts = store.listTemporalFacts(ctx.workspace.id, {
1652
+ includeHistorical,
1653
+ query: query || undefined,
1654
+ });
1655
+ console.log(JSON.stringify({
1656
+ ok: true,
1657
+ activeFacts: facts.filter((f) => f.validTo === null),
1658
+ historicalFacts: facts.filter((f) => f.validTo !== null),
1659
+ facts,
1660
+ }, null, 2));
1661
+ return;
1662
+ }
1663
+ if (sub === "forget") {
1664
+ const query = positional.slice(2).join(" ").trim() || String(flags.query || "").trim();
1665
+ if (!query)
1666
+ throw new Error("Usage: muxo memory forget <text>");
1667
+ if (remoteUrl) {
1668
+ const res = await remoteFetch("/api/v1/memory/forget", {
1669
+ method: "POST",
1670
+ body: { query },
1671
+ });
1672
+ console.log(JSON.stringify(res, null, 2));
1673
+ return;
1674
+ }
1675
+ const { engine, ctx } = getLocalContext();
1676
+ const pruned = await engine.forgetWorkspaceMemoryMatching(ctx, query);
1677
+ console.log(JSON.stringify({ ok: true, ...pruned }, null, 2));
1678
+ return;
1679
+ }
783
1680
  if (sub === "add") {
784
1681
  const content = positional.slice(2).join(" ") || String(flags.content || "");
785
1682
  if (!content)
786
1683
  throw new Error("Usage: muxo memory add <text>");
1684
+ if (remoteUrl) {
1685
+ const res = await remoteFetch("/api/v1/memory", {
1686
+ method: "POST",
1687
+ body: {
1688
+ content,
1689
+ title: flags.title ? String(flags.title) : content.slice(0, 60),
1690
+ },
1691
+ });
1692
+ console.log(JSON.stringify(res, null, 2));
1693
+ return;
1694
+ }
1695
+ const { engine, ctx } = getLocalContext();
787
1696
  const entry = await engine.addWorkspaceMemory(ctx, {
788
1697
  content,
789
1698
  title: flags.title ? String(flags.title) : content.slice(0, 60),
@@ -791,6 +1700,34 @@ async function runCli() {
791
1700
  console.log(JSON.stringify({ ok: true, memory: entry }, null, 2));
792
1701
  return;
793
1702
  }
1703
+ if (sub === "delete" || sub === "rm" || sub === "remove") {
1704
+ const id = positional[2] || String(flags.id || "");
1705
+ if (!id)
1706
+ throw new Error("Usage: muxo memory delete <memory_id>");
1707
+ if (remoteUrl) {
1708
+ const res = await remoteFetch(`/api/v1/memory/${encodeURIComponent(id)}`, { method: "DELETE" });
1709
+ console.log(JSON.stringify(res, null, 2));
1710
+ return;
1711
+ }
1712
+ const { engine, ctx } = getLocalContext();
1713
+ const deleted = await engine.deleteWorkspaceMemory(ctx, id);
1714
+ console.log(JSON.stringify({ ok: true, ...deleted }, null, 2));
1715
+ return;
1716
+ }
1717
+ if (remoteUrl) {
1718
+ const query = sub === "recall" || sub === "search"
1719
+ ? positional.slice(2).join(" ") || flags.query || undefined
1720
+ : positional.slice(1).join(" ") || flags.query || undefined;
1721
+ const suffix = query ? `?query=${encodeURIComponent(query)}` : "";
1722
+ const res = await remoteFetch(`/api/v1/memory${suffix}`);
1723
+ console.log(JSON.stringify({
1724
+ ok: true,
1725
+ ...res,
1726
+ localMemories: res.records || [],
1727
+ }, null, 2));
1728
+ return;
1729
+ }
1730
+ const { engine, ctx } = getLocalContext();
794
1731
  const query = sub === "recall" || sub === "search"
795
1732
  ? positional.slice(2).join(" ") || flags.query || undefined
796
1733
  : positional.slice(1).join(" ") || flags.query || undefined;
@@ -803,13 +1740,125 @@ async function runCli() {
803
1740
  return;
804
1741
  }
805
1742
  if (cmd === "governance") {
806
- const status = engine.governance.getGovernanceStatus(ctx.workspace);
807
- const summary = engine.governance.getStatus(ctx.workspace);
1743
+ const sub = positional[1];
1744
+ const ringLabel = (policy) => policy?.executionRing !== undefined
1745
+ ? RING_LABELS[policy.executionRing] || `Ring ${policy.executionRing}`
1746
+ : "Per action (set by risk level)";
1747
+ if (sub === "pause" || sub === "kill-switch" || sub === "kill" || sub === "quarantine") {
1748
+ const reason = positional.slice(2).join(" ").trim() ||
1749
+ (typeof flags.reason === "string" ? flags.reason : "") ||
1750
+ "Paused from the Muxo CLI";
1751
+ if (remoteUrl) {
1752
+ const res = await remoteFetch("/api/v1/governance/kill-switch", {
1753
+ method: "POST",
1754
+ body: { action: "quarantine", reason },
1755
+ });
1756
+ console.log(JSON.stringify(res, null, 2));
1757
+ return;
1758
+ }
1759
+ const { engine, ctx } = getLocalContext();
1760
+ const out = await engine.activateWorkspaceKillSwitch(ctx, { reason });
1761
+ console.log(JSON.stringify({ ok: true, ...out }, null, 2));
1762
+ return;
1763
+ }
1764
+ if (sub === "resume") {
1765
+ if (remoteUrl) {
1766
+ const res = await remoteFetch("/api/v1/governance/kill-switch", {
1767
+ method: "POST",
1768
+ body: { action: "resume" },
1769
+ });
1770
+ console.log(JSON.stringify(res, null, 2));
1771
+ return;
1772
+ }
1773
+ const { engine, ctx } = getLocalContext();
1774
+ const out = engine.resumeWorkspaceKillSwitch(ctx);
1775
+ console.log(JSON.stringify({ ok: true, ...out }, null, 2));
1776
+ return;
1777
+ }
1778
+ if (sub === "scan") {
1779
+ const content = positional.slice(2).join(" ").trim() || String(flags.content || "").trim();
1780
+ if (!content)
1781
+ throw new Error("Usage: muxo governance scan <text>");
1782
+ if (remoteUrl) {
1783
+ const res = await remoteFetch("/api/v1/governance/scan-context", {
1784
+ method: "POST",
1785
+ body: { content, sessionId: "cli" },
1786
+ });
1787
+ console.log(JSON.stringify(res, null, 2));
1788
+ return;
1789
+ }
1790
+ const { engine, ctx } = getLocalContext();
1791
+ const scan = engine.governance.scanMemoryContent({
1792
+ workspaceId: ctx.workspace.id,
1793
+ sessionId: "cli",
1794
+ role: "user",
1795
+ content,
1796
+ });
1797
+ console.log(JSON.stringify({ ok: true, scan }, null, 2));
1798
+ return;
1799
+ }
1800
+ if (sub === "policy") {
1801
+ const patch = {};
1802
+ if (typeof flags.mode === "string")
1803
+ patch.mode = flags.mode;
1804
+ if (flags.ring !== undefined)
1805
+ patch.executionRing = Number(flags.ring);
1806
+ if (typeof flags["require-approval"] === "string") {
1807
+ const raw = flags["require-approval"].trim();
1808
+ patch.requireApprovalFor =
1809
+ raw === "none" ? [] : raw.split(",").map((r) => r.trim()).filter(Boolean);
1810
+ }
1811
+ if (flags["block-destructive-sql"] !== undefined) {
1812
+ patch.blockDestructiveSql =
1813
+ flags["block-destructive-sql"] === true || flags["block-destructive-sql"] === "true";
1814
+ }
1815
+ if (flags["max-credits"] !== undefined)
1816
+ patch.maxCreditsPerRun = Number(flags["max-credits"]);
1817
+ if (Object.keys(patch).length === 0) {
1818
+ if (remoteUrl) {
1819
+ const res = await remoteFetch("/api/v1/governance");
1820
+ const policy = res.workspacePolicy || {};
1821
+ console.log(JSON.stringify({ ok: true, ringName: ringLabel(policy), policy }, null, 2));
1822
+ return;
1823
+ }
1824
+ const { store, ctx } = getLocalContext();
1825
+ const policy = (store.getWorkspace(ctx.workspace.id) || ctx.workspace).policy;
1826
+ console.log(JSON.stringify({ ok: true, ringName: ringLabel(policy), policy }, null, 2));
1827
+ return;
1828
+ }
1829
+ if (remoteUrl) {
1830
+ const res = await remoteFetch("/api/v1/policy", { method: "POST", body: patch });
1831
+ const policy = res.workspace?.policy || {};
1832
+ console.log(JSON.stringify({ ok: true, ringName: ringLabel(policy), policy }, null, 2));
1833
+ return;
1834
+ }
1835
+ const { store, ctx } = getLocalContext();
1836
+ const updated = store.updateWorkspacePolicy(ctx.workspace.id, sanitizePolicyPatch(patch));
1837
+ console.log(JSON.stringify({ ok: true, ringName: ringLabel(updated.policy), policy: updated.policy }, null, 2));
1838
+ return;
1839
+ }
1840
+ if (remoteUrl) {
1841
+ const res = await remoteFetch("/api/v1/governance");
1842
+ console.log(JSON.stringify({
1843
+ ok: true,
1844
+ did: res.governance?.agentDid,
1845
+ chainValid: res.governance?.auditChain?.chainVerified ??
1846
+ res.governance?.auditIntegrityValid ??
1847
+ true,
1848
+ ringName: ringLabel(res.workspacePolicy),
1849
+ ...res,
1850
+ }, null, 2));
1851
+ return;
1852
+ }
1853
+ const { engine, ctx, store } = getLocalContext();
1854
+ const ws = store.getWorkspace(ctx.workspace.id) || ctx.workspace;
1855
+ const status = engine.governance.getGovernanceStatus(ws);
1856
+ const summary = engine.governance.getStatus(ws);
808
1857
  console.log(JSON.stringify({
809
1858
  ok: true,
810
1859
  did: summary.agentDid,
811
1860
  chainValid: summary.auditChainIntegrity,
812
- ringName: "Ring 2 (Standard)",
1861
+ ringName: ringLabel(ws.policy),
813
1862
  ...status,
814
1863
  }, null, 2));
815
1864
  return;
@@ -841,23 +1890,44 @@ async function runCli() {
841
1890
  // Natural Language fallback: if user passed plain English directly (`muxo "Audit https://muxo.ai for SEO"`)
842
1891
  if (cmd !== "help" && !flags.help && !flags.h && positional.length > 0) {
843
1892
  const promptText = positional.join(" ").trim();
1893
+ const planOnly = flags["plan-only"] === true || flags.plan === true;
1894
+ const autoApprove = flags["no-approve"] ? false : true;
1895
+ const fundingMode = flags.byok || flags.funding === "byok"
1896
+ ? "byok"
1897
+ : flags.funding === "muxo_funded"
1898
+ ? "muxo_funded"
1899
+ : undefined;
1900
+ if (remoteUrl) {
1901
+ const data = await remoteFetch("/api/v1/do", {
1902
+ method: "POST",
1903
+ body: {
1904
+ prompt: promptText,
1905
+ dryRun: planOnly,
1906
+ planOnly,
1907
+ autoApprove,
1908
+ autoExecute: !planOnly,
1909
+ approveRisky: autoApprove,
1910
+ fundingMode,
1911
+ },
1912
+ });
1913
+ console.log(JSON.stringify(data, null, 2));
1914
+ return;
1915
+ }
1916
+ const { engine, ctx } = getLocalContext();
844
1917
  const result = await engine.doNaturalLanguage(ctx, {
845
1918
  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,
1919
+ autoApprove,
1920
+ planOnly,
1921
+ fundingMode,
853
1922
  });
854
1923
  console.log(JSON.stringify(result, null, 2));
855
1924
  return;
856
1925
  }
857
1926
  console.log(`
858
- Muxo CLI — Human & LLM Friendly Execution, Infrastructure, BYOK & Governance Platform
1927
+ Muxo CLI — Unified Agent Execution, Cloud Infrastructure, Memory & Safety Platform
859
1928
 
860
1929
  Natural Language & Interactive TUI:
1930
+ muxo setup 1-command auto-signup + MCP setup (.mcp.json + ~/.muxo/config.json)
861
1931
  muxo do "<instruction>" Execute any goal in plain English (LLM/semantic router -> plan -> execute)
862
1932
  muxo ask "<instruction>" [--plan-only] Alias for 'muxo do' (pass --plan-only to inspect plan before running)
863
1933
  muxo tui Launch interactive Terminal UI dashboard (BYOK, Billing, OAuth, Infra)
@@ -867,28 +1937,40 @@ Core Commands:
867
1937
  muxo login [--api-key <key> | --signup] Sign in or create an autonomous workspace (~/.muxo/config.json)
868
1938
  muxo logout Remove saved CLI authentication config
869
1939
  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
1940
+ muxo whoami Show workspace profile, plan, agent identity, and credit balance
1941
+ muxo search <query> [--lane <lane>] Search across Instant Tools, Connected Apps, Cloud Infra & Billing
872
1942
  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
1943
+ muxo connections [--sync true|false] List Connected Apps & Cloud Infrastructure provider connections
874
1944
  muxo link <provider> [--wait] Start OAuth handoff for a service and optionally wait
875
1945
  muxo connect <provider> [--wait] Alias for 'muxo link'
876
1946
  muxo wait <provider> [--timeout 15] Poll until a pending OAuth connection handoff becomes active
877
1947
  muxo plan <tool_slug> --input '{...}' Create a governed execution plan (accepts natural language too)
878
1948
  muxo approve <plan_id> Approve a pending high-risk plan
879
- muxo run <plan_id> Execute an approved plan and issue a signed receipt
1949
+ muxo run <plan_id> Execute an approved plan and issue a verifiable audit record
880
1950
  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
1951
+ muxo resources List provisioned Muxo Cloud Infrastructure resources
882
1952
  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
1953
+ muxo receipts [<receipt_id>] List or verify execution audit records
884
1954
  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
1955
+ muxo memory [<query>] Recall Muxo Workspace Memory & hybrid context
1956
+ muxo memory add <text> | delete <id> Save or delete a workspace memory
1957
+ muxo memory facts [<query>] [--current] List temporal facts with their history (or only current facts)
1958
+ muxo memory forget <text> Delete memories and retire facts that match <text>
1959
+ muxo governance Inspect Muxo Safety & Approvals identity and audit chain
1960
+ muxo governance pause [<reason>] | resume Turn the workspace kill switch on or off
1961
+ muxo governance scan <text> Check text for prompt injection before storing or sending it
1962
+ muxo governance policy [--mode enforce|monitor] [--ring 0-3] [--require-approval write,admin|none]
1963
+ [--block-destructive-sql true|false] [--max-credits <n>]
1964
+ muxo harness Check API, MCP, auth, governance and catalog; print MCP config
887
1965
  muxo serve [--port 4310] Start unified HTTP Console, Landing, OAuth Callback & MCP server
888
1966
  muxo mcp Run stdio MCP server (20 canonical tools)
889
1967
  `);
890
1968
  }
891
- runCli().catch((err) => {
1969
+ runCli()
1970
+ .then(() => {
1971
+ process.exit(Number(process.exitCode ?? 0));
1972
+ })
1973
+ .catch((err) => {
892
1974
  console.error("Error:", err?.message || err);
893
1975
  process.exit(1);
894
1976
  });