@brutalsystems/muster 0.7.16 → 0.7.17
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/README.md +6 -2
- package/dist/format.js +26 -0
- package/dist/muster.js +27 -3
- package/dist/run.js +15 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -892,8 +892,12 @@ selected external tool may have its own write or network capabilities.
|
|
|
892
892
|
Installed deliberately, in the one session that should hold spawn authority —
|
|
893
893
|
never at user scope.
|
|
894
894
|
|
|
895
|
-
Muster exposes `run`, `list`, `stop`, and `output` over stdio.
|
|
896
|
-
|
|
895
|
+
Muster exposes `run`, `list`, `stop`, and `output` over stdio. `mcp` starts the
|
|
896
|
+
server, and so does no arguments when stdin is not a terminal — which is what an
|
|
897
|
+
MCP client provides. Typing `muster` at a terminal prints the usage text and a
|
|
898
|
+
short status instead, because a server waiting for JSON-RPC on stdin is
|
|
899
|
+
indistinguishable from a hang. `muster mcp` starts the server either way, so it
|
|
900
|
+
can still be driven by hand to debug it. Diagnostics go to stderr, never protocol stdout.
|
|
897
901
|
Schemas match the CLI (`args` is the array of optional runtime arguments).
|
|
898
902
|
|
|
899
903
|
For a single Codex session, use per-invocation configuration:
|
package/dist/format.js
CHANGED
|
@@ -100,3 +100,29 @@ export function formatHuman(value, color = false) {
|
|
|
100
100
|
: "No Muster runs.\n";
|
|
101
101
|
return recordHuman(value, color) + "\n";
|
|
102
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* What a human sees when they type `muster` with no arguments at a terminal.
|
|
105
|
+
*
|
|
106
|
+
* With no arguments Muster starts the MCP server, which from a terminal looks
|
|
107
|
+
* exactly like a hang — the process is waiting for JSON-RPC on stdin and has
|
|
108
|
+
* nothing to say. The server path is reached by a pipe, never a TTY, so a
|
|
109
|
+
* terminal can be answered without changing what any MCP client gets.
|
|
110
|
+
*
|
|
111
|
+
* The session count is what the registry recorded, not what is running: it is
|
|
112
|
+
* read without the refresh `list` performs, because that refresh also reaps a
|
|
113
|
+
* terminal entry's identity copy and reaches an OpenCode session over HTTP.
|
|
114
|
+
* Neither belongs in the output of typing a bare command, so the wording says
|
|
115
|
+
* "recorded" and points at `list` for the authoritative answer.
|
|
116
|
+
*/
|
|
117
|
+
export function statusText(identities, sessions) {
|
|
118
|
+
const lines = [];
|
|
119
|
+
if (!identities.length)
|
|
120
|
+
lines.push("No identities yet — add one with:", " muster setup-identity --identity NAME --agent <codex|claude|opencode> --interactive");
|
|
121
|
+
else
|
|
122
|
+
lines.push(`${identities.length} ${identities.length === 1 ? "identity" : "identities"} — ` +
|
|
123
|
+
identities.map((i) => `${i.name} ${i.auth.state}`).join(", "));
|
|
124
|
+
lines.push(sessions === 0
|
|
125
|
+
? "No recorded sessions."
|
|
126
|
+
: `${sessions} recorded session${sessions === 1 ? "" : "s"} (muster list refreshes)`, "", "Starting the MCP server? Use `muster mcp`.");
|
|
127
|
+
return lines.join("\n") + "\n";
|
|
128
|
+
}
|
package/dist/muster.js
CHANGED
|
@@ -7,7 +7,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
|
|
|
7
7
|
import { z } from "zod";
|
|
8
8
|
import { Muster } from "./run.js";
|
|
9
9
|
import { delay } from "./identity/processes.js";
|
|
10
|
-
import { formatHuman, humanColorEnabled } from "./format.js";
|
|
10
|
+
import { formatHuman, humanColorEnabled, statusText } from "./format.js";
|
|
11
11
|
import { checkPlugins, npmRegistry } from "./doctor.js";
|
|
12
12
|
import { loadConfig, musterHome } from "./config.js";
|
|
13
13
|
import { describeIdentities, interactiveSetup, setupIdentity, } from "./identity-cli.js";
|
|
@@ -102,14 +102,38 @@ async function main() {
|
|
|
102
102
|
process.stdout.write(`${version}\n`);
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
// No arguments starts the MCP server: a documented entry point, and how a
|
|
106
|
+
// client configured with `command: "muster"` and no args reaches it. From a
|
|
107
|
+
// terminal that is indistinguishable from a hang — the process is waiting for
|
|
108
|
+
// JSON-RPC on stdin and has nothing to say. A client always arrives down a
|
|
109
|
+
// pipe and never a TTY, so a person can be answered without changing what any
|
|
110
|
+
// client gets. `muster mcp` stays unconditional, so the server can still be
|
|
111
|
+
// driven by hand in a terminal to debug it.
|
|
112
|
+
const bareAtTerminal = argv.length === 0 && Boolean(process.stdin.isTTY);
|
|
113
|
+
const mcp = !bareAtTerminal && (argv.length === 0 || argv[0] === "mcp");
|
|
114
|
+
if (argv[0] === "mcp" && argv.length > 1)
|
|
107
115
|
throw new Error("mcp takes no arguments");
|
|
108
116
|
const muster = await Muster.create();
|
|
109
117
|
let shutdown;
|
|
110
118
|
const close = () => (shutdown ??= (async () => {
|
|
111
119
|
await muster.close();
|
|
112
120
|
})());
|
|
121
|
+
if (bareAtTerminal) {
|
|
122
|
+
try {
|
|
123
|
+
const cfg = await loadConfig();
|
|
124
|
+
const view = await describeIdentities(musterHome(), process.env, Object.fromEntries(Object.entries(cfg.requester_profiles).map(([n, p]) => [
|
|
125
|
+
n,
|
|
126
|
+
p.identities,
|
|
127
|
+
])));
|
|
128
|
+
process.stdout.write(help +
|
|
129
|
+
"\n" +
|
|
130
|
+
statusText(view.identities, (await muster.summary()).sessions));
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
await close();
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
113
137
|
for (const signal of ["SIGTERM", "SIGINT", "SIGHUP"])
|
|
114
138
|
process.once(signal, () => {
|
|
115
139
|
void close().then(() => process.exit(0), (e) => {
|
package/dist/run.js
CHANGED
|
@@ -823,6 +823,21 @@ export class Muster {
|
|
|
823
823
|
* with the supported entry point (#44), which is also what would need to
|
|
824
824
|
* choose between hiding other requesters' entries and redacting them.
|
|
825
825
|
*/
|
|
826
|
+
/**
|
|
827
|
+
* Counts for the status line a bare `muster` prints at a terminal.
|
|
828
|
+
*
|
|
829
|
+
* Deliberately not `list`. That refreshes every entry, and a refresh removes
|
|
830
|
+
* a terminal entry's identity copy and MCP config and reaches an OpenCode
|
|
831
|
+
* session over HTTP — none of which belongs in the output of typing a bare
|
|
832
|
+
* command. This reads the registry and changes nothing, so what it reports is
|
|
833
|
+
* what was recorded rather than what is running, and the caller says so.
|
|
834
|
+
*/
|
|
835
|
+
async summary() {
|
|
836
|
+
const entries = await this.registry.all();
|
|
837
|
+
return {
|
|
838
|
+
sessions: entries.filter((e) => e.kind === "session" && ["starting", "running"].includes(e.status)).length,
|
|
839
|
+
};
|
|
840
|
+
}
|
|
826
841
|
async list(kind) {
|
|
827
842
|
const entries = await this.registry.all();
|
|
828
843
|
const result = [];
|