@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.
- package/.agents/plugins/marketplace.json +1 -1
- package/.claude-plugin/marketplace.json +19 -4
- package/.codex/config.toml +9 -2
- package/README.md +63 -20
- package/dist/clerk.d.ts +33 -0
- package/dist/clerk.js +198 -0
- package/dist/cli.js +1181 -99
- package/dist/engine.d.ts +22 -1
- package/dist/engine.js +284 -14
- package/dist/mcp.d.ts +6 -1
- package/dist/mcp.js +109 -22
- package/dist/nl.js +60 -36
- package/dist/policy.d.ts +9 -0
- package/dist/policy.js +73 -0
- package/dist/providers/composio-instant.d.ts +4 -0
- package/dist/providers/composio-instant.js +634 -534
- package/dist/providers/composio-sdk.d.ts +1 -0
- package/dist/providers/composio-sdk.js +6 -2
- package/dist/providers/microsoft-governance.d.ts +61 -2
- package/dist/providers/microsoft-governance.js +458 -24
- package/dist/providers/stripe-billing.js +14 -4
- package/dist/providers/stripe-projects.d.ts +4 -1
- package/dist/providers/stripe-projects.js +123 -12
- package/dist/providers/supermemory.d.ts +28 -5
- package/dist/providers/supermemory.js +183 -55
- package/dist/sdk.d.ts +167 -1
- package/dist/sdk.js +113 -2
- package/dist/server.js +258 -40
- package/dist/store.d.ts +26 -1
- package/dist/store.js +255 -6
- package/dist/types.d.ts +86 -0
- package/dist/ui/console-html.d.ts +16 -0
- package/dist/ui/console-html.js +2427 -2206
- package/dist/ui/design-tokens.d.ts +177 -0
- package/dist/ui/design-tokens.js +210 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +2 -2
- 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,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:
|
|
81
|
-
:
|
|
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
|
-
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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:
|
|
225
|
-
version:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
|
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
|
|
1318
|
+
const data = await remoteFetch("/api/v1/byok", {
|
|
556
1319
|
method: "POST",
|
|
557
|
-
|
|
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(
|
|
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
|
|
581
|
-
|
|
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
|
|
598
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 === "
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
const
|
|
660
|
-
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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
|
|
807
|
-
const
|
|
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:
|
|
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
|
|
847
|
-
planOnly
|
|
848
|
-
fundingMode
|
|
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 —
|
|
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,
|
|
871
|
-
muxo search <query> [--lane <lane>] Search across Instant,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
886
|
-
muxo
|
|
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()
|
|
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
|
});
|