Skip to content

Commit b32ed1b

Browse files
committed
fix(tui): tighten /connect error reporting for edge cases
Two silent-failure cases in /connect could leave users without any feedback to act on: - Reject `--url` when its value is missing (e.g. `/connect --url` or `/connect --url=`). Previously the argument parser silently fell back to the default catalog, so a malformed flag still appeared to succeed but with the wrong source. - Show an explicit error when the resolved catalog yields no providers with supported wire types. Previously the picker resolved with no selection and the command returned without any UI feedback.
1 parent c4ffdb2 commit b32ed1b

3 files changed

Lines changed: 101 additions & 32 deletions

File tree

apps/kimi-code/src/tui/kimi-tui.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5096,7 +5096,12 @@ export class KimiTUI {
50965096
// (context size, capabilities) comes from the catalog, so users do not
50975097
// hand-write it.
50985098
private async handleConnectCommand(args: string): Promise<void> {
5099-
const { url, preferBuiltIn, allowBuiltInFallback } = resolveConnectCatalogRequest(args);
5099+
const resolution = resolveConnectCatalogRequest(args);
5100+
if (resolution.kind === 'error') {
5101+
this.showError(resolution.message);
5102+
return;
5103+
}
5104+
const { url, preferBuiltIn, allowBuiltInFallback } = resolution.request;
51005105

51015106
let catalog: Catalog | undefined;
51025107

@@ -5315,6 +5320,7 @@ export class KimiTUI {
53155320
.toSorted((a, b) => a.label.localeCompare(b.label));
53165321

53175322
if (options.length === 0) {
5323+
this.showError('Catalog has no providers with supported wire types.');
53185324
resolve(undefined);
53195325
return;
53205326
}
Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { DEFAULT_CATALOG_URL } from '@moonshot-ai/kimi-code-sdk';
22

33
const CATALOG_URL_FLAG_RE = /--url(?:=|\s+)(\S+)/;
4+
const URL_FLAG_PRESENT_RE = /(?:^|\s)--url(?=\s|=|$)/;
45
const REFRESH_FLAG_RE = /(?:^|\s)--refresh(?=\s|$)/;
56
const BARE_HTTP_URL_RE = /^https?:\/\/\S+$/;
67

@@ -10,24 +11,41 @@ export interface ConnectCatalogRequest {
1011
readonly allowBuiltInFallback: boolean;
1112
}
1213

13-
export function resolveConnectCatalogRequest(args: string): ConnectCatalogRequest {
14+
export type ConnectCatalogResolution =
15+
| { readonly kind: 'ok'; readonly request: ConnectCatalogRequest }
16+
| { readonly kind: 'error'; readonly message: string };
17+
18+
export function resolveConnectCatalogRequest(args: string): ConnectCatalogResolution {
1419
const trimmed = args.trim();
1520
const urlMatch = CATALOG_URL_FLAG_RE.exec(trimmed);
1621
const bareUrl = BARE_HTTP_URL_RE.test(trimmed) ? trimmed : undefined;
1722
const explicitUrl = urlMatch?.[1] ?? bareUrl;
1823

1924
if (explicitUrl !== undefined) {
2025
return {
21-
url: explicitUrl,
22-
preferBuiltIn: false,
23-
allowBuiltInFallback: false,
26+
kind: 'ok',
27+
request: {
28+
url: explicitUrl,
29+
preferBuiltIn: false,
30+
allowBuiltInFallback: false,
31+
},
32+
};
33+
}
34+
35+
if (URL_FLAG_PRESENT_RE.test(trimmed)) {
36+
return {
37+
kind: 'error',
38+
message: '--url requires a value, e.g. /connect --url=https://example.com/catalog.json',
2439
};
2540
}
2641

2742
const refreshRequested = REFRESH_FLAG_RE.test(trimmed);
2843
return {
29-
url: DEFAULT_CATALOG_URL,
30-
preferBuiltIn: !refreshRequested,
31-
allowBuiltInFallback: true,
44+
kind: 'ok',
45+
request: {
46+
url: DEFAULT_CATALOG_URL,
47+
preferBuiltIn: !refreshRequested,
48+
allowBuiltInFallback: true,
49+
},
3250
};
3351
}

apps/kimi-code/test/tui/utils/connect-catalog.test.ts

Lines changed: 69 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,52 +13,97 @@ import { builtInCatalogDefine } from '../../../scripts/built-in-catalog.mjs';
1313
describe('resolveConnectCatalogRequest', () => {
1414
it('prefers the built-in catalog by default and keeps online fetch as fallback', () => {
1515
expect(resolveConnectCatalogRequest('')).toEqual({
16-
url: DEFAULT_CATALOG_URL,
17-
preferBuiltIn: true,
18-
allowBuiltInFallback: true,
16+
kind: 'ok',
17+
request: {
18+
url: DEFAULT_CATALOG_URL,
19+
preferBuiltIn: true,
20+
allowBuiltInFallback: true,
21+
},
1922
});
2023
expect(resolveConnectCatalogRequest('ignored text')).toEqual({
21-
url: DEFAULT_CATALOG_URL,
22-
preferBuiltIn: true,
23-
allowBuiltInFallback: true,
24+
kind: 'ok',
25+
request: {
26+
url: DEFAULT_CATALOG_URL,
27+
preferBuiltIn: true,
28+
allowBuiltInFallback: true,
29+
},
2430
});
2531
});
2632

2733
it('forces an online fetch when --refresh is requested', () => {
2834
expect(resolveConnectCatalogRequest('--refresh')).toEqual({
29-
url: DEFAULT_CATALOG_URL,
30-
preferBuiltIn: false,
31-
allowBuiltInFallback: true,
35+
kind: 'ok',
36+
request: {
37+
url: DEFAULT_CATALOG_URL,
38+
preferBuiltIn: false,
39+
allowBuiltInFallback: true,
40+
},
3241
});
3342
expect(resolveConnectCatalogRequest(' --refresh ')).toEqual({
34-
url: DEFAULT_CATALOG_URL,
35-
preferBuiltIn: false,
36-
allowBuiltInFallback: true,
43+
kind: 'ok',
44+
request: {
45+
url: DEFAULT_CATALOG_URL,
46+
preferBuiltIn: false,
47+
allowBuiltInFallback: true,
48+
},
3749
});
3850
});
3951

4052
it('treats explicit catalog URLs as authoritative and ignores --refresh on them', () => {
4153
expect(resolveConnectCatalogRequest('--url=https://internal.example/catalog.json')).toEqual({
42-
url: 'https://internal.example/catalog.json',
43-
preferBuiltIn: false,
44-
allowBuiltInFallback: false,
54+
kind: 'ok',
55+
request: {
56+
url: 'https://internal.example/catalog.json',
57+
preferBuiltIn: false,
58+
allowBuiltInFallback: false,
59+
},
4560
});
4661
expect(resolveConnectCatalogRequest('--url https://internal.example/catalog.json')).toEqual({
47-
url: 'https://internal.example/catalog.json',
48-
preferBuiltIn: false,
49-
allowBuiltInFallback: false,
62+
kind: 'ok',
63+
request: {
64+
url: 'https://internal.example/catalog.json',
65+
preferBuiltIn: false,
66+
allowBuiltInFallback: false,
67+
},
5068
});
5169
expect(resolveConnectCatalogRequest('https://internal.example/catalog.json')).toEqual({
52-
url: 'https://internal.example/catalog.json',
53-
preferBuiltIn: false,
54-
allowBuiltInFallback: false,
70+
kind: 'ok',
71+
request: {
72+
url: 'https://internal.example/catalog.json',
73+
preferBuiltIn: false,
74+
allowBuiltInFallback: false,
75+
},
5576
});
5677
expect(
5778
resolveConnectCatalogRequest('--refresh --url=https://internal.example/catalog.json'),
5879
).toEqual({
59-
url: 'https://internal.example/catalog.json',
60-
preferBuiltIn: false,
61-
allowBuiltInFallback: false,
80+
kind: 'ok',
81+
request: {
82+
url: 'https://internal.example/catalog.json',
83+
preferBuiltIn: false,
84+
allowBuiltInFallback: false,
85+
},
86+
});
87+
});
88+
89+
it('rejects --url when no value is provided', () => {
90+
const expectedMessage =
91+
'--url requires a value, e.g. /connect --url=https://example.com/catalog.json';
92+
expect(resolveConnectCatalogRequest('--url')).toEqual({
93+
kind: 'error',
94+
message: expectedMessage,
95+
});
96+
expect(resolveConnectCatalogRequest('--url=')).toEqual({
97+
kind: 'error',
98+
message: expectedMessage,
99+
});
100+
expect(resolveConnectCatalogRequest(' --url ')).toEqual({
101+
kind: 'error',
102+
message: expectedMessage,
103+
});
104+
expect(resolveConnectCatalogRequest('--refresh --url')).toEqual({
105+
kind: 'error',
106+
message: expectedMessage,
62107
});
63108
});
64109
});

0 commit comments

Comments
 (0)