Add device pairing command and voice mute toggle
Introduce `paseo daemon pair` CLI command that generates a local pairing offer URL with QR code without requiring a running daemon. Update message input to support mute/unmute toggle during realtime voice sessions with visual feedback (MicOff icon). Fix android:prod build script to use prebuild + assembleRelease flow.
This commit is contained in:
@@ -4,6 +4,7 @@ export { loadConfig, type CliConfigOverrides } from "./config.js";
|
||||
export { resolvePaseoHome } from "./paseo-home.js";
|
||||
export { createRootLogger, type LogLevel, type LogFormat } from "./logger.js";
|
||||
export { loadPersistedConfig, type PersistedConfig } from "./persisted-config.js";
|
||||
export { generateLocalPairingOffer, type LocalPairingOffer } from "./pairing-offer.js";
|
||||
export { DaemonClient, type DaemonClientConfig, type ConnectionState, type DaemonEvent } from "../client/daemon-client.js";
|
||||
|
||||
// Agent SDK types for CLI commands
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { Logger } from "pino";
|
||||
|
||||
import { createConnectionOfferV2, encodeOfferToFragmentUrl } from "./connection-offer.js";
|
||||
import { loadOrCreateDaemonKeyPair } from "./daemon-keypair.js";
|
||||
import { renderPairingQr } from "./pairing-qr.js";
|
||||
import { getOrCreateServerId } from "./server-id.js";
|
||||
|
||||
export type LocalPairingOffer = {
|
||||
relayEnabled: boolean;
|
||||
url: string | null;
|
||||
qr: string | null;
|
||||
};
|
||||
|
||||
export async function generateLocalPairingOffer(args: {
|
||||
paseoHome: string;
|
||||
relayEnabled?: boolean;
|
||||
relayEndpoint?: string;
|
||||
relayPublicEndpoint?: string;
|
||||
appBaseUrl?: string;
|
||||
includeQr?: boolean;
|
||||
logger?: Logger;
|
||||
}): Promise<LocalPairingOffer> {
|
||||
const relayEnabled = args.relayEnabled ?? true;
|
||||
if (!relayEnabled) {
|
||||
return {
|
||||
relayEnabled: false,
|
||||
url: null,
|
||||
qr: null,
|
||||
};
|
||||
}
|
||||
|
||||
const relayEndpoint = args.relayEndpoint ?? "relay.paseo.sh:443";
|
||||
const relayPublicEndpoint = args.relayPublicEndpoint ?? relayEndpoint;
|
||||
const appBaseUrl = args.appBaseUrl ?? "https://app.paseo.sh";
|
||||
const serverId = getOrCreateServerId(args.paseoHome, { logger: args.logger });
|
||||
const daemonKeyPair = await loadOrCreateDaemonKeyPair(args.paseoHome, args.logger);
|
||||
const offer = await createConnectionOfferV2({
|
||||
serverId,
|
||||
daemonPublicKeyB64: daemonKeyPair.publicKeyB64,
|
||||
relay: { endpoint: relayPublicEndpoint },
|
||||
});
|
||||
const url = encodeOfferToFragmentUrl({ offer, appBaseUrl });
|
||||
|
||||
if (args.includeQr === false) {
|
||||
return {
|
||||
relayEnabled: true,
|
||||
url,
|
||||
qr: null,
|
||||
};
|
||||
}
|
||||
|
||||
let qr: string | null = null;
|
||||
try {
|
||||
qr = await renderPairingQr(url);
|
||||
} catch (error) {
|
||||
args.logger?.debug({ error }, "Failed to render pairing QR");
|
||||
}
|
||||
|
||||
return {
|
||||
relayEnabled: true,
|
||||
url,
|
||||
qr,
|
||||
};
|
||||
}
|
||||
@@ -16,7 +16,7 @@ function shouldPrintPairingQr(): boolean {
|
||||
return Boolean(process.stdout.isTTY);
|
||||
}
|
||||
|
||||
async function renderQr(url: string): Promise<string> {
|
||||
export async function renderPairingQr(url: string): Promise<string> {
|
||||
const terminalOptions: QRCodeToStringOptionsTerminal = {
|
||||
type: "terminal",
|
||||
small: true,
|
||||
@@ -39,7 +39,7 @@ export async function printPairingQrIfEnabled(args: {
|
||||
}): Promise<void> {
|
||||
if (!shouldPrintPairingQr()) return;
|
||||
|
||||
const qr = await renderQr(args.url);
|
||||
const qr = await renderPairingQr(args.url);
|
||||
const out = `\nScan to pair:\n${qr}\n${args.url}\n`;
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user