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