feat(ui): replace subcommands with interactive menu

/remote-control now opens a select menu with Turn on/off,
Configure URL, and Status instead of relying on subcommands.
Adds ability to stop the server. Shows current URL in the
Configure URL menu item and in the input dialog title.
This commit is contained in:
Yejun Su 2026-03-19 12:32:26 +08:00
parent ee3341d20c
commit 7080cdc34f
No known key found for this signature in database
GPG Key ID: AD03A563F321CA44
2 changed files with 87 additions and 63 deletions

View File

@ -6,22 +6,12 @@
pi install https://github.com/goofansu/pi-remote-control pi install https://github.com/goofansu/pi-remote-control
``` ```
## Setup ## Usage
### 1. Configure your public URL Run `/remote-control` to open the menu:
Tell the extension the base URL your proxy/tunnel exposes: - **Turn on / Turn off** — start or stop the server
- **Configure URL** — set the public base URL your proxy/tunnel exposes (saved to `~/.pi/agent/remote-control.json`)
- **Status** — show the QR code and connection URL (only when server is running)
``` On first use, you'll be prompted to configure the URL before the server starts.
/remote-control config
```
Enter something like `http://pi.myhost` or `https://pi.example.com`. This is saved to `~/.pi/agent/remote-control.json`.
### 2. Start the server
```
/remote-control
```
This starts the server on a random localhost port, generates an auth token, and displays a QR code + URL. Open the URL on any device.

View File

@ -103,7 +103,10 @@ async function configureRemoteControlUI(ctx: ExtensionContext): Promise<void> {
if (!ctx.hasUI) return; if (!ctx.hasUI) return;
const current = (await readRemoteControlConfig()).publicBaseUrl ?? ""; const current = (await readRemoteControlConfig()).publicBaseUrl ?? "";
const raw = await ctx.ui.input("Remote-control public base URL", current || "e.g. http://pi.sgponte"); const title = current
? `Public base URL (current: ${current})`
: "Public base URL";
const raw = await ctx.ui.input(title, "e.g. http://pi.myhost");
if (raw === undefined) return; if (raw === undefined) return;
let value: string; let value: string;
@ -1215,29 +1218,13 @@ export default function remoteControl(pi: ExtensionAPI) {
// ── /remote-control command ─────────────────────────────────────────────── // ── /remote-control command ───────────────────────────────────────────────
pi.registerCommand("remote-control", { async function showConnectionInfo(ctx: ExtensionContext): Promise<void> {
description: "Start localhost-only remote control server for use behind a port-forwarding proxy", if (!server) return;
handler: async (args, ctx) => {
if (!ctx.hasUI) return;
const subcommand = args.trim().toLowerCase();
if (subcommand === "config") {
await configureRemoteControlUI(ctx);
return;
}
const config = await readRemoteControlConfig(); const config = await readRemoteControlConfig();
const publicBaseUrl = config.publicBaseUrl?.trim(); const publicBaseUrl = config.publicBaseUrl?.trim();
if (!publicBaseUrl) { if (!publicBaseUrl) return;
ctx.ui.notify("Set the public URL first with /remote-control config", "warning");
return;
}
// Start server on first invocation
if (!server) {
server = await startServer(pi, ctx);
server.onClientChange(() => updateStatus(ctx));
updateStatus(ctx);
}
const url = buildRemoteControlUrl(publicBaseUrl, server.port, server.token); const url = buildRemoteControlUrl(publicBaseUrl, server.port, server.token);
// Generate QR code // Generate QR code
@ -1271,6 +1258,53 @@ export default function remoteControl(pi: ExtensionAPI) {
}, },
}; };
}); });
}
pi.registerCommand("remote-control", {
description: "Remote control — start/stop server, configure, show connection info",
handler: async (args, ctx) => {
if (!ctx.hasUI) return;
const isRunning = !!server;
const config = await readRemoteControlConfig();
const currentUrl = config.publicBaseUrl?.trim();
const configLabel = currentUrl ? `Configure URL (${currentUrl})` : "Configure URL (not set)";
const menuItems = [
isRunning ? "Turn off" : "Turn on",
configLabel,
...(isRunning ? ["Status"] : []),
];
const choice = await ctx.ui.select("Remote control", menuItems);
if (choice === undefined) return;
if (choice === "Turn on") {
const publicBaseUrl = currentUrl;
if (!publicBaseUrl) {
ctx.ui.notify("Set the public URL first — opening config…", "warning");
await configureRemoteControlUI(ctx);
// Re-check after config
const updated = await readRemoteControlConfig();
if (!updated.publicBaseUrl?.trim()) return;
}
server = await startServer(pi, ctx);
server.onClientChange(() => updateStatus(ctx));
updateStatus(ctx);
ctx.ui.notify("Remote-control server started", "info");
await showConnectionInfo(ctx);
} else if (choice === "Turn off") {
if (server) {
await server.stop();
server = undefined;
ctx.ui.setStatus("remote-control", undefined);
ctx.ui.notify("Remote-control server stopped", "info");
}
} else if (choice === configLabel) {
await configureRemoteControlUI(ctx);
} else if (choice === "Status") {
await showConnectionInfo(ctx);
}
}, },
}); });
} }