-
Notifications
You must be signed in to change notification settings - Fork 37
feat(kimi-code): add /connect command with bundled model catalog #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
81633ed
f3a10d8
2247086
9ef1cc6
082f09d
401125f
a50f2ae
c4ffdb2
b32ed1b
226f6a3
311da42
7407461
bad6c80
f037ea6
2b7b875
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| "@moonshot-ai/kosong": minor | ||
| "@moonshot-ai/kimi-code-sdk": minor | ||
| "@moonshot-ai/kimi-code": minor | ||
| --- | ||
|
|
||
| Add a `/connect` command that configures a provider and model from a model catalog. By default it reads from a pruned catalog snapshot bundled with the CLI, so the command works offline and is not gated by models.dev availability. Model metadata (context window, output limit, and capabilities) is filled in automatically, so models no longer need to be written by hand in config. Pass `--refresh` to fetch the latest catalog from models.dev (falling back to the bundled snapshot on failure), or `--url` to point at a custom catalog endpoint that uses the same format. When connecting an Anthropic-compatible provider whose catalog base URL already includes a version segment, the request path no longer duplicates that segment, so connections that previously failed with a not-found error now succeed. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": minor | ||
| --- | ||
|
|
||
| The `/connect` provider and model pickers now support type-to-search filtering, and long selection lists are paginated instead of rendering every entry at once. The model picker also paginates when many models are configured. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Show a hint pointing to /login (Kimi) and /connect (other providers) when /model is opened with no configured models, and surface the same hint on the welcome panel when no model is set. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { readFileSync } from 'node:fs'; | ||
|
|
||
| export const BUILT_IN_CATALOG_ENV = 'KIMI_CODE_BUILT_IN_CATALOG_FILE'; | ||
| export const BUILT_IN_CATALOG_DEFINE = '__KIMI_CODE_BUILT_IN_CATALOG__'; | ||
|
|
||
| export function builtInCatalogDefine(env = process.env) { | ||
| const file = env[BUILT_IN_CATALOG_ENV]; | ||
| if (file === undefined || file.length === 0) return 'undefined'; | ||
| return JSON.stringify(readFileSync(file, 'utf-8')); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| #!/usr/bin/env node | ||
| /** | ||
| * Fetches models.dev/api.json, strips fields not needed by kimi-code, and | ||
| * writes the result as raw JSON for release builds to inline. | ||
| * | ||
| * This script intentionally does not write into src/. The source tree keeps a | ||
| * placeholder so the generated catalog is not committed. | ||
| */ | ||
|
|
||
| import { mkdirSync, writeFileSync } from "node:fs"; | ||
| import { dirname, resolve } from "node:path"; | ||
|
|
||
| const scriptDir = import.meta.dirname; | ||
| const outFile = resolveOutputFile(process.argv.slice(2)); | ||
| const modelsUrl = process.env.MODELS_DEV_URL || "https://models.dev/api.json"; | ||
|
|
||
| const KEEP_PROVIDER = new Set(["id", "name", "api", "env", "npm", "type", "models"]); | ||
| const KEEP_MODEL = new Set(["id", "name", "family", "limit", "tool_call", "reasoning", "modalities"]); | ||
|
|
||
| function resolveOutputFile(args) { | ||
| const index = args.indexOf("--out"); | ||
| if (index !== -1) { | ||
| const value = args[index + 1]; | ||
| if (value === undefined || value.length === 0) { | ||
| throw new Error("Missing value for --out"); | ||
| } | ||
| return resolve(process.cwd(), value); | ||
| } | ||
| return resolve(scriptDir, "../dist/built-in-catalog.json"); | ||
| } | ||
|
|
||
| function stripModel(model) { | ||
| if (typeof model !== "object" || model === null) return undefined; | ||
| const result = {}; | ||
| for (const key of Object.keys(model)) { | ||
| if (KEEP_MODEL.has(key)) result[key] = model[key]; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| function stripProvider(provider) { | ||
| if (typeof provider !== "object" || provider === null) return undefined; | ||
| const result = {}; | ||
| for (const key of Object.keys(provider)) { | ||
| if (!KEEP_PROVIDER.has(key)) continue; | ||
| const value = provider[key]; | ||
| if (key === "models") { | ||
| const stripped = {}; | ||
| for (const [mId, m] of Object.entries(value)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The catalog-stripper assumes every provider's Useful? React with 👍 / 👎. |
||
| const s = stripModel(m); | ||
| if (s !== undefined) stripped[mId] = s; | ||
| } | ||
| if (Object.keys(stripped).length > 0) result[key] = stripped; | ||
| } else { | ||
| result[key] = value; | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| async function fetchCatalog(url) { | ||
| const res = await fetch(url, { headers: { Accept: "application/json" } }); | ||
| if (!res.ok) throw new Error(`HTTP ${res.status}`); | ||
| const raw = await res.json(); | ||
| if (typeof raw !== "object" || raw === null || Array.isArray(raw)) { | ||
| throw new Error("invalid payload shape"); | ||
| } | ||
| const stripped = {}; | ||
| for (const [k, v] of Object.entries(raw)) { | ||
| const p = stripProvider(v); | ||
| if (p !== undefined && Object.keys(p).length > 0) stripped[k] = p; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The build script writes provider IDs from fetched JSON directly into a plain object. If the upstream/mirrored catalog contains a key like Useful? React with 👍 / 👎. |
||
| } | ||
| return JSON.stringify(stripped); | ||
| } | ||
|
|
||
| async function main() { | ||
| console.log(`Fetching ${modelsUrl} ...`); | ||
| const json = await fetchCatalog(modelsUrl); | ||
| mkdirSync(dirname(outFile), { recursive: true }); | ||
| writeFileSync(outFile, json, "utf-8"); | ||
| console.log(`Wrote ${outFile} (${(json.length / 1024).toFixed(0)} KB JSON)`); | ||
| } | ||
|
|
||
| main().catch((error) => { | ||
| console.error(error.message); | ||
| process.exit(1); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // Filled by tsdown define in release builds. Source stays empty so the | ||
| // generated models.dev snapshot is not committed. | ||
| declare const __KIMI_CODE_BUILT_IN_CATALOG__: string | undefined; | ||
|
|
||
| export const BUILT_IN_CATALOG_JSON: string | undefined = | ||
| typeof __KIMI_CODE_BUILT_IN_CATALOG__ === 'string' | ||
| ? __KIMI_CODE_BUILT_IN_CATALOG__ | ||
| : undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The release workflow now unconditionally fetches
models.devbefore any package build/publish step, so a transient outage or network failure at that endpoint fails the entire monorepo release job (including unrelated packages). Because the catalog is only needed for embedding into Kimi Code artifacts, this new always-on external dependency can halt normal release operations even when no Kimi Code package is being shipped.Useful? React with 👍 / 👎.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By design —
/connectoffline-by-default is a release-time commitment. If the catalog can't be fetched, the produced binaries would silently lose offline behavior, which is a degraded release and should block. The right response to a transient models.dev outage here is to retry the release, not decouple the dependency. Coupling unrelated packages (kosong, agent-core, etc.) to catalog availability is the cost of a single release pipeline; we prefer that over silently shipping degraded kimi-code.