From ef4f54a19343019e34c46c93820a909b0964a32f Mon Sep 17 00:00:00 2001 From: "haozhe.yang" Date: Thu, 10 Sep 2026 14:26:22 +0800 Subject: [PATCH 1/3] fix(remote-control): carry server token in rc local UI link - add required localServerToken to RemoteControlOutputOptions - render the Local UI line with buildOpenableUrl and dim the #token= fragment like the ready banner - pass the resolved token at both rc call sites (kimi web --rc, TUI /rc) --- apps/kimi-code/src/cli/sub/web/remote-control.ts | 8 +++++++- apps/kimi-code/src/cli/sub/web/run.ts | 1 + apps/kimi-code/src/tui/commands/web.ts | 1 + apps/kimi-code/test/cli/web/remote-control.test.ts | 4 ++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/kimi-code/src/cli/sub/web/remote-control.ts b/apps/kimi-code/src/cli/sub/web/remote-control.ts index 4aab921c9c6..d719767768b 100644 --- a/apps/kimi-code/src/cli/sub/web/remote-control.ts +++ b/apps/kimi-code/src/cli/sub/web/remote-control.ts @@ -4,6 +4,7 @@ import { getVersion } from '../../version'; import { darkColors } from '../../../tui/theme/colors'; import { supportsHyperlinks, toTerminalHyperlink } from '../../../utils/terminal-hyperlink'; import type { RemoteControlStatus } from '@moonshot-ai/remote-control'; +import { buildOpenableUrl, splitTokenFragment } from './access-urls'; export { acquireRemoteControlLock, @@ -32,6 +33,7 @@ export type { export interface RemoteControlOutputOptions { readonly url: string; readonly localOrigin: string; + readonly localServerToken: string; readonly deviceName: string; readonly qrCode: string; readonly pngPath: string; @@ -41,12 +43,16 @@ export function formatRemoteControlOutput(options: RemoteControlOutputOptions): const title = (text: string): string => chalk.bold.hex(darkColors.primary)(text); const label = (text: string): string => chalk.bold.hex(darkColors.textDim)(text); const accent = (text: string): string => chalk.hex(darkColors.accent)(text); + const dim = (text: string): string => chalk.hex(darkColors.textDim)(text); const muted = (text: string): string => chalk.hex(darkColors.textMuted)(text); const status = (text: string): string => chalk.hex(darkColors.success)(text); const link = (url: string): string => supportsHyperlinks() ? toTerminalHyperlink(accent(url), url) : accent(url); const docs = toTerminalHyperlink('docs', 'https://kimi.com/code/docs/remote-control'); const feedback = toTerminalHyperlink('feedback', 'https://kimi.com/code/feedback'); + const [localBase, localFrag] = splitTokenFragment( + buildOpenableUrl(options.localOrigin, options.localServerToken), + ); return [ '', ` ${title('Kimi Remote Control ready')} ${muted(getVersion())}`, @@ -62,7 +68,7 @@ export function formatRemoteControlOutput(options: RemoteControlOutputOptions): '', options.qrCode.trimEnd().replaceAll(/^/gm, ' '), ` ${label('QR code PNG: ')}${options.pngPath} ${muted('(open this if the QR above does not scan)')}`, - ` ${label('Local UI: ')}${muted(options.localOrigin)} ${muted('(LAN: --host)')}`, + ` ${label('Local UI: ')}${accent(localBase)}${dim(localFrag)} ${muted('(LAN: --host)')}`, '', ` ${docs} ${muted('·')} ${feedback}`, ` ${label('Logs: ')}${muted('off (--log-level info)')} ${muted('·')} ${label('Stop: ')}${muted('Ctrl+C')}`, diff --git a/apps/kimi-code/src/cli/sub/web/run.ts b/apps/kimi-code/src/cli/sub/web/run.ts index 878ab3cfc60..b10a666d9cd 100644 --- a/apps/kimi-code/src/cli/sub/web/run.ts +++ b/apps/kimi-code/src/cli/sub/web/run.ts @@ -233,6 +233,7 @@ export async function handleWebCommand( formatRemoteControlOutput({ url: remoteControl.url, localOrigin: origin, + localServerToken: token, deviceName: remoteControl.deviceName, qrCode: qrCode.terminal, pngPath: qrCode.pngPath, diff --git a/apps/kimi-code/src/tui/commands/web.ts b/apps/kimi-code/src/tui/commands/web.ts index 80ac0a62274..95c310d71ec 100644 --- a/apps/kimi-code/src/tui/commands/web.ts +++ b/apps/kimi-code/src/tui/commands/web.ts @@ -80,6 +80,7 @@ export async function handleRemoteControlCommand(host: SlashCommandHost): Promis formatRemoteControlOutput({ url, localOrigin: origin, + localServerToken: token, deviceName: remoteControl.deviceName, qrCode: qrCode.terminal, pngPath: qrCode.pngPath, diff --git a/apps/kimi-code/test/cli/web/remote-control.test.ts b/apps/kimi-code/test/cli/web/remote-control.test.ts index 1d0804631e2..cc0f8ba9c78 100644 --- a/apps/kimi-code/test/cli/web/remote-control.test.ts +++ b/apps/kimi-code/test/cli/web/remote-control.test.ts @@ -13,6 +13,7 @@ describe('Remote Control output', () => { const outputOptions = { url: 'https://example.test/devices/example-device/?rc=1&from=kimi_code_cli', localOrigin: 'http://127.0.0.1:1234', + localServerToken: 'example-token', deviceName: 'example-device', qrCode: 'QR\n', pngPath: '/tmp/example-qr.png', @@ -32,6 +33,8 @@ describe('Remote Control output', () => { .replaceAll(/\u001B\[[0-9;]*m/g, ''); expect(plain).toContain(`open ${url}`); expect(plain).not.toContain('exampl…'); + expect(plain).toContain('http://127.0.0.1:1234/#token=example-token'); + expect(output).toContain('#token=example-token'); expect(output).toContain('Connected to example.test'); expect(output).toContain('This device:'); expect(output).not.toContain('Manage devices'); @@ -48,6 +51,7 @@ describe('Remote Control output', () => { vi.stubEnv('FORCE_HYPERLINK', '0'); const output = formatRemoteControlOutput(outputOptions); expect(output).toContain(`open ${outputOptions.url}`); + expect(output).toContain('#token=example-token'); expect(output).not.toContain('exampl…vice'); expect(output).not.toContain('Manage devices'); }); From 31cb1570e609ce0eba2be8d71ba9491877c41794 Mon Sep 17 00:00:00 2001 From: "haozhe.yang" Date: Thu, 10 Sep 2026 14:44:36 +0800 Subject: [PATCH 2/3] chore(changeset): add changeset for rc local UI token fix --- .changeset/rc-local-ui-token.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rc-local-ui-token.md diff --git a/.changeset/rc-local-ui-token.md b/.changeset/rc-local-ui-token.md new file mode 100644 index 00000000000..4730554447e --- /dev/null +++ b/.changeset/rc-local-ui-token.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Include the server token in the Remote Control Local UI link so it opens already signed in. From 2d89e050b72bc143f9cbfbde46bdd3c9b85f7f6f Mon Sep 17 00:00:00 2001 From: "haozhe.yang" Date: Thu, 10 Sep 2026 14:55:01 +0800 Subject: [PATCH 3/3] fix(remote-control): align rc output test with token-bearing local UI link - assert the Local UI line carries the #token= fragment - scope the token-free ban to the relay and session URLs, which must never leak the local server token --- apps/kimi-code/test/tui/commands/web.test.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/kimi-code/test/tui/commands/web.test.ts b/apps/kimi-code/test/tui/commands/web.test.ts index 31d0b7307d7..d31e10ca416 100644 --- a/apps/kimi-code/test/tui/commands/web.test.ts +++ b/apps/kimi-code/test/tui/commands/web.test.ts @@ -243,8 +243,11 @@ describe('handleRemoteControlCommand', () => { const png = readFileSync(pngPath); expect(png.subarray(0, 8)).toEqual(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])); expect(png).toEqual(await QRCode.toBuffer(sessionUrl)); - expect(written).not.toContain('local-server-token'); - expect(written).not.toContain('#token='); + expect(written).toContain( + 'Local UI: http://127.0.0.1:58627/#token=local-server-token', + ); + expect(written).not.toContain(`${entryUrl}#token=`); + expect(written).not.toContain(`${sessionUrl}#token=`); expect(close).toHaveBeenCalledOnce(); } finally { writeSpy.mockRestore();