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/provider-replaces-connect-hints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@moonshot-ai/kimi-code": patch
---

Point users to `/provider` instead of the removed `/connect` command in the welcome screen and the no-models-configured hint.
122 changes: 2 additions & 120 deletions apps/kimi-code/src/tui/commands/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,22 @@ import {
type ManagedKimiConfigShape,
type OpenPlatformDefinition,
} from '@moonshot-ai/kimi-code-oauth';
import {
applyCatalogProvider,
catalogBaseUrl,
catalogProviderModels,
CatalogFetchError,
fetchCatalog,
inferWireType,
loadBuiltInCatalog,
log,
type Catalog,
} from '@moonshot-ai/kimi-code-sdk';
import { log } from '@moonshot-ai/kimi-code-sdk';

import { BUILT_IN_CATALOG_JSON } from '../../built-in-catalog';
import type { ChoiceOption } from '../components/dialogs/choice-picker';
import { DEFAULT_OAUTH_PROVIDER_NAME, PRODUCT_NAME } from '../constant/kimi-tui';
import { resolveConnectCatalogRequest } from '../utils/connect-catalog';
import { formatErrorMessage } from '../utils/event-payload';
import type { LoginProgressSpinnerHandle } from '../types';
import {
promptApiKey,
promptCatalogProviderSelection,
promptLogoutProviderSelection,
promptModelSelectionForCatalog,
promptModelSelectionForOpenPlatform,
promptPlatformSelection,
} from './prompts';
import type { SlashCommandHost } from './dispatch';

// ---------------------------------------------------------------------------
// Auth: login / logout / connect
// Auth: login / logout
// ---------------------------------------------------------------------------

export async function handleLoginCommand(host: SlashCommandHost): Promise<void> {
Expand Down Expand Up @@ -188,110 +174,6 @@ async function handleOpenPlatformLogin(
host.showStatus(`Setup complete: ${platform.name} · ${selection.model.id}`);
}

export async function handleConnectCommand(host: SlashCommandHost, args: string): Promise<void> {
const resolution = resolveConnectCatalogRequest(args);
if (resolution.kind === 'error') {
host.showError(resolution.message);
return;
}
const { url, preferBuiltIn, allowBuiltInFallback } = resolution.request;

let catalog: Catalog | undefined;

if (preferBuiltIn) {
const builtIn = loadBuiltInCatalog(BUILT_IN_CATALOG_JSON);
if (builtIn !== undefined) {
host.showStatus('Loaded built-in catalog. Run /connect refresh for the latest.');
catalog = builtIn;
}
}

if (catalog === undefined) {
const controller = new AbortController();
const cancel = (): void => {
controller.abort();
};
host.cancelInFlight = cancel;

const spinner = host.showLoginProgressSpinner(`Fetching catalog from ${url}`);
try {
catalog = await fetchCatalog(url, controller.signal);
spinner.stop({ ok: true, label: 'Catalog loaded.' });
} catch (error) {
if (controller.signal.aborted) {
spinner.stop({ ok: false, label: 'Aborted.' });
} else {
const hint = error instanceof CatalogFetchError ? ` (HTTP ${error.status})` : '';
if (!allowBuiltInFallback) {
spinner.stop({ ok: false, label: 'Failed to load catalog.' });
host.showError(`Failed to fetch catalog${hint}: ${formatErrorMessage(error)}`);
} else {
const fallback = loadBuiltInCatalog(BUILT_IN_CATALOG_JSON);
if (fallback !== undefined) {
spinner.stop({ ok: true, label: 'Using built-in catalog (offline mode).' });
catalog = fallback;
} else {
spinner.stop({ ok: false, label: 'Failed to load catalog.' });
host.showError(`Failed to fetch catalog${hint}: ${formatErrorMessage(error)}`);
}
}
}
} finally {
if (host.cancelInFlight === cancel) host.cancelInFlight = undefined;
}
}

if (catalog === undefined) return;

const providerId = await promptCatalogProviderSelection(host, catalog);
if (providerId === undefined) return;
const entry = catalog[providerId];
if (entry === undefined) return;

const models = catalogProviderModels(entry);
if (models.length === 0) {
host.showError(`Provider "${providerId}" has no usable models in this catalog.`);
return;
}

const selection = await promptModelSelectionForCatalog(host, providerId, models);
if (selection === undefined) return;

const apiKey = await promptApiKey(host, entry.name ?? providerId);
if (apiKey === undefined) return;

const wire = inferWireType(entry);
if (wire === undefined) return;
const baseUrl = catalogBaseUrl(entry, wire);

const existingConfig = await host.harness.getConfig();
if (existingConfig.providers[providerId] !== undefined) {
await host.harness.removeProvider(providerId);
}

const config = await host.harness.getConfig();
applyCatalogProvider(config, {
providerId,
wire,
baseUrl,
apiKey,
models,
selectedModelId: selection.model.id,
thinking: selection.thinking,
});

await host.harness.setConfig({
providers: config.providers,
models: config.models,
defaultModel: config.defaultModel,
defaultThinking: config.defaultThinking,
});

await host.authFlow.refreshConfigAfterLogin();
host.track('connect', { provider: providerId, model: selection.model.id });
host.showStatus(`Connected: ${entry.name ?? providerId} · ${selection.model.id}`);
}

export async function handleLogoutCommand(host: SlashCommandHost): Promise<void> {
const oauthStatus = await host.harness.auth.status(DEFAULT_OAUTH_PROVIDER_NAME);
const hasOAuthToken = oauthStatus.providers.some(
Expand Down
2 changes: 1 addition & 1 deletion apps/kimi-code/src/tui/commands/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ export function showModelPicker(host: SlashCommandHost, selectedValue: string =
if (entries.length === 0) {
host.showNotice(
'No models configured',
'Run /login to sign in to Kimi, or /connect to add another provider from a model catalog.',
'Run /login to sign in to Kimi, or /provider to add another provider from a model catalog.',
);
return;
}
Expand Down
1 change: 0 additions & 1 deletion apps/kimi-code/src/tui/commands/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import {
// ---------------------------------------------------------------------------

export {
handleConnectCommand,
handleLoginCommand,
handleLogoutCommand,
} from './auth';
Expand Down
1 change: 0 additions & 1 deletion apps/kimi-code/src/tui/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ export * from './types';

export { dispatchInput, type SlashCommandHost } from './dispatch';
export {
handleConnectCommand,
handleLoginCommand,
handleLogoutCommand,
} from './auth';
Expand Down
4 changes: 2 additions & 2 deletions apps/kimi-code/src/tui/components/chrome/welcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class WelcomeComponent implements Component {
const dim = chalk.hex(this.colors.textDim);
const labelStyle = chalk.bold.hex(this.colors.textDim);
const rightRow1 = truncateToWidth(
dim(isLoggedOut ? 'Run /login or /connect to get started.' : 'Send /help for help information.'),
dim(isLoggedOut ? 'Run /login or /provider to get started.' : 'Send /help for help information.'),
textWidth,
'…',
);
Expand All @@ -57,7 +57,7 @@ export class WelcomeComponent implements Component {

const activeModel = this.state.availableModels[this.state.model];
const modelValue = isLoggedOut
? chalk.hex(this.colors.warning)('not set, run /login or /connect')
? chalk.hex(this.colors.warning)('not set, run /login or /provider')
: (activeModel?.displayName ?? activeModel?.model ?? this.state.model);

const infoLines = [
Expand Down
82 changes: 0 additions & 82 deletions apps/kimi-code/src/tui/utils/connect-catalog.ts

This file was deleted.

Loading
Loading