Skip to content

Commit abf1895

Browse files
committed
feat: add mux api subcommand for direct oRPC access
Integrates trpc-cli to expose the oRPC router as CLI commands, enabling scripted interactions without running the full desktop app or server. Services are lazily initialized only when the api subcommand is invoked to avoid startup overhead for other commands.
1 parent 7535578 commit abf1895

File tree

1 file changed

+80
-25
lines changed

1 file changed

+80
-25
lines changed

src/cli/index.ts

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,91 @@
22

33
import { Command } from "commander";
44
import { VERSION } from "../version";
5+
import { createCli } from "trpc-cli";
6+
import { router } from "@/node/orpc/router";
7+
import { Config } from "@/node/config";
8+
import { ServiceContainer } from "@/node/services/serviceContainer";
9+
import { migrateLegacyMuxHome } from "@/common/constants/paths";
10+
import type { BrowserWindow } from "electron";
11+
import type { ORPCContext } from "@/node/orpc/context";
512

6-
const program = new Command();
13+
// Minimal BrowserWindow stub for services that expect one (same as server.ts)
14+
const mockWindow: BrowserWindow = {
15+
isDestroyed: () => false,
16+
setTitle: () => undefined,
17+
webContents: {
18+
send: () => undefined,
19+
openDevTools: () => undefined,
20+
},
21+
} as unknown as BrowserWindow;
722

8-
program
9-
.name("mux")
10-
.description("mux - coder multiplexer")
11-
.version(`mux ${VERSION.git_describe} (${VERSION.git_commit})`, "-v, --version");
23+
async function createServiceContext(): Promise<ORPCContext> {
24+
migrateLegacyMuxHome();
1225

13-
program
14-
.command("server")
15-
.description("Start the HTTP/WebSocket oRPC server")
16-
.allowUnknownOption() // server.ts handles its own options via commander
17-
.action(() => {
18-
// Remove 'server' from args since server.ts has its own commander instance
19-
process.argv.splice(2, 1);
26+
const config = new Config();
27+
const serviceContainer = new ServiceContainer(config);
28+
await serviceContainer.initialize();
29+
serviceContainer.windowService.setMainWindow(mockWindow);
30+
31+
return {
32+
projectService: serviceContainer.projectService,
33+
workspaceService: serviceContainer.workspaceService,
34+
providerService: serviceContainer.providerService,
35+
terminalService: serviceContainer.terminalService,
36+
windowService: serviceContainer.windowService,
37+
updateService: serviceContainer.updateService,
38+
tokenizerService: serviceContainer.tokenizerService,
39+
serverService: serviceContainer.serverService,
40+
menuEventService: serviceContainer.menuEventService,
41+
};
42+
}
43+
44+
async function main() {
45+
const program = new Command();
46+
47+
program
48+
.name("mux")
49+
.description("mux - coder multiplexer")
50+
.version(`mux ${VERSION.git_describe} (${VERSION.git_commit})`, "-v, --version");
51+
52+
program
53+
.command("server")
54+
.description("Start the HTTP/WebSocket oRPC server")
55+
.allowUnknownOption() // server.ts handles its own options via commander
56+
.action(() => {
57+
// Remove 'server' from args since server.ts has its own commander instance
58+
process.argv.splice(2, 1);
59+
// eslint-disable-next-line @typescript-eslint/no-require-imports
60+
require("./server");
61+
});
62+
63+
program
64+
.command("version")
65+
.description("Show version information")
66+
.action(() => {
67+
console.log(`mux ${VERSION.git_describe} (${VERSION.git_commit})`);
68+
});
69+
70+
// Only initialize services if the 'api' subcommand is being used
71+
if (process.argv[2] === "api") {
72+
const context = await createServiceContext();
73+
program.addCommand(
74+
(createCli({ router: router(), context }).buildProgram() as Command)
75+
.name("api")
76+
.description("Interact with the oRPC API directly")
77+
);
78+
}
79+
80+
// Default action: launch desktop app when no subcommand given
81+
program.action(() => {
2082
// eslint-disable-next-line @typescript-eslint/no-require-imports
21-
require("./server");
83+
require("../desktop/main");
2284
});
2385

24-
program
25-
.command("version")
26-
.description("Show version information")
27-
.action(() => {
28-
console.log(`mux ${VERSION.git_describe} (${VERSION.git_commit})`);
29-
});
86+
program.parse();
87+
}
3088

31-
// Default action: launch desktop app when no subcommand given
32-
program.action(() => {
33-
// eslint-disable-next-line @typescript-eslint/no-require-imports
34-
require("../desktop/main");
89+
main().catch((error) => {
90+
console.error("CLI initialization failed:", error);
91+
process.exit(1);
3592
});
36-
37-
program.parse();

0 commit comments

Comments
 (0)