Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/web-open-wildcard-host.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Open the browser on localhost instead of the wildcard bind address for `kimi web --host 0.0.0.0`.
12 changes: 12 additions & 0 deletions apps/kimi-code/src/cli/sub/web/access-urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ function isWildcard(host: string): boolean {
return host === '' || host === '0.0.0.0' || host === '::';
}

/**
* Rewrite a bound origin for browser auto-open. A wildcard bind host
* (`0.0.0.0` / `::` / empty) is not navigable, so open localhost on the same
* port instead — the same address the ready banner's `Local:` line shows.
*/
export function browserOpenOrigin(origin: string): string {
const separator = origin.lastIndexOf(':');
const host = origin.slice(origin.indexOf('://') + 3, separator);
if (!isWildcard(host)) return origin;
return `http://localhost${origin.slice(separator)}`;
}

/** True when `host` is a loopback address (this host only). */
export function isLoopbackHost(host: string): boolean {
return host === 'localhost' || host === '127.0.0.1' || host === '::1';
Expand Down
6 changes: 4 additions & 2 deletions apps/kimi-code/src/cli/sub/web/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from '../../version';
import {
accessUrlLines,
browserOpenOrigin,
buildOpenableUrl,
isLoopbackHost,
splitTokenFragment,
Expand Down Expand Up @@ -260,7 +261,8 @@ export async function handleWebCommand(
: formatReadyLine(origin, token, parsed.dangerousBypassAuth),
);
if (opts.open === true) {
deps.openUrl(token !== undefined ? buildWebUrl(origin, token) : origin);
const openOrigin = browserOpenOrigin(origin);
deps.openUrl(token !== undefined ? buildWebUrl(openOrigin, token) : openOrigin);
}
},
onShutdown: async () => {
Expand Down Expand Up @@ -473,7 +475,7 @@ export function formatReadyBanner(
return frag === '' ? url(base) : url(base) + dim(frag);
};

const port = Number(new URL(origin).port);
const port = Number(origin.slice(origin.lastIndexOf(':') + 1));
// Borderless header: the Kimi sprite (the little mascot with eyes) sits next
// to the title, keeping the brand without the enclosing box.
const logo = ['▐█▛█▛█▌', '▐█████▌'] as const;
Expand Down
55 changes: 55 additions & 0 deletions apps/kimi-code/test/cli/web/web.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,46 @@ describe('`kimi web` opens the browser', () => {
expect(openUrl).toHaveBeenCalledWith('http://127.0.0.1:58627');
});

it('opens localhost rather than the wildcard bind address', async () => {
const { handleWebCommand } = await import('#/cli/sub/web/run');
const { runner } = makeRunner('http://0.0.0.0:58627');
const { stdout, stderr } = makeIo();
const openUrl = vi.fn();

await handleWebCommand(
{ host: '0.0.0.0', open: true },
{
startServerForeground: runner,
resolveToken: () => 'tok-xyz',
openUrl,
stdout,
stderr,
},
);

expect(openUrl).toHaveBeenCalledWith('http://localhost:58627/#token=tok-xyz');
});

it('opens localhost for a wildcard IPv6 bind', async () => {
const { handleWebCommand } = await import('#/cli/sub/web/run');
const { runner } = makeRunner('http://:::58627');
const { stdout, stderr } = makeIo();
const openUrl = vi.fn();

await handleWebCommand(
{ host: '::', open: true },
{
startServerForeground: runner,
resolveToken: () => undefined,
openUrl,
stdout,
stderr,
},
);

expect(openUrl).toHaveBeenCalledWith('http://localhost:58627');
});

it('does not open the browser when open is false', async () => {
const { handleWebCommand } = await import('#/cli/sub/web/run');
const { runner } = makeRunner('http://127.0.0.1:9000');
Expand Down Expand Up @@ -1030,6 +1070,21 @@ describe('accessUrlLines', () => {
});
});

describe('browserOpenOrigin', () => {
it('rewrites wildcard bind hosts to localhost on the same port', async () => {
const { browserOpenOrigin } = await import('#/cli/sub/web/access-urls');
expect(browserOpenOrigin('http://0.0.0.0:58627')).toBe('http://localhost:58627');
expect(browserOpenOrigin('http://:::58627')).toBe('http://localhost:58627');
});

it('keeps navigable origins unchanged', async () => {
const { browserOpenOrigin } = await import('#/cli/sub/web/access-urls');
expect(browserOpenOrigin('http://127.0.0.1:58627')).toBe('http://127.0.0.1:58627');
expect(browserOpenOrigin('http://192.168.1.5:58627')).toBe('http://192.168.1.5:58627');
expect(browserOpenOrigin('http://[::1]:58627')).toBe('http://[::1]:58627');
});
});

describe('`kimi web rotate-token`', () => {
let dir: string;
let prevHome: string | undefined;
Expand Down
Loading