Skip to content

feat(cli): setup-continue — generate ~/.continue/config.yaml for OmniRoute - #4289

Merged
diegosouzapw merged 1 commit into
release/v3.8.30from
feat/continue-cli
Jun 19, 2026
Merged

diegosouzapw merged 1 commit into
release/v3.8.30from
feat/continue-cli

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

What

CLI #6. omniroute setup-continue generates Continue's file-based, mergeable ~/.continue/config.yaml (shared by VS Code/JetBrains extensions and the cn CLI) from the live OmniRoute catalog.

  • Each curated model → provider: openai, model: <id>, apiBase WITH /v1 (Continue appends /chat/completions), apiKey: ${{ secrets.OMNIROUTE_API_KEY }} (referenced, never written), roles: [chat, edit, apply] (+ autocomplete for the fast tier).
  • Merges existing config.yaml via js-yaml (drops prior models on the same apiBase, preserves other models + top-level keys).
  • Remote-aware; --only filter; prints how to supply the key (shell env for cn; ~/.continue/.env for IDE).

Researched: provider: openai + custom apiBase (with /v1), the ${{ secrets.X }} syntax, roles, and the unified cn CLI config. (js-yaml 4 load is safe-by-default; the file is the user's own.)

Validation

  • Remote (VPS v3.8.30): setup-continue --remote --only glm,kimi --dry-run → correct YAML.
  • Continue's wire (/v1/chat/completions) already validated → "OK".
  • 6 unit tests; check:cli-i18n ✓.

Series: Codex #4270 · Claude #4274 · OpenCode #4277 · hardening #4278 · Cline #4280 · Kilo #4284 · Continue (this). Next: Cursor, Roo, Crush, Goose, Qwen Code, Gemini CLI, Aider.

🤖 Generated with Claude Code

…Route

CLI #6 of the series. `omniroute setup-continue` writes Continue's file-based,
mergeable ~/.continue/config.yaml (shared by the VS Code/JetBrains extensions AND
the `cn` CLI) from the live model catalog.

- Each curated model → a Continue model entry: provider: openai, model: <id>,
  apiBase WITH /v1 (Continue appends /chat/completions), apiKey:
  ${{ secrets.OMNIROUTE_API_KEY }} (secret referenced, never written), roles
  [chat, edit, apply] (+ autocomplete for the fast tier).
- Merges into existing config.yaml (js-yaml load/dump): drops prior models on the
  same apiBase, preserves the user's other models + top-level keys.
- Remote-aware (--remote/--api-key → active context → localhost); --only filter.
- Prints how to provide the key (shell env for cn; ~/.continue/.env for IDE).

Researched against current Continue docs: provider: openai + custom apiBase (with
/v1), the ${{ secrets.X }} syntax, roles, and that the `cn` CLI shares the same
config. Continue's wire (/v1/chat/completions) already validated → "OK".

Tests: buildContinueModels (provider/apiBase/secret/roles, fast→autocomplete,
skip uncategorised), mergeContinueConfig (replace-ours/keep-others/defaults),
resolveContinueTarget (/v1). 6 unit tests; check:cli-i18n green.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the setup-continue CLI command to configure Continue (continue.dev) for OmniRoute, along with corresponding documentation and unit tests. The review feedback highlights opportunities to improve robustness, specifically by ensuring null/undefined elements are filtered out to prevent invalid YAML generation, using optional chaining to avoid crashes if the model response body is null, and refactoring the config backup logic to avoid redundant file reads and handle potential write errors.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

export function mergeContinueConfig(existing, newModels, apiBase) {
const cfg = existing && typeof existing === "object" ? { ...existing } : {};
const prior = Array.isArray(cfg.models) ? cfg.models : [];
const kept = prior.filter((m) => !m || m.apiBase !== apiBase);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If m is null or undefined, !m evaluates to true, which means null/undefined elements are kept in the kept array. This will result in null entries in the final models array, producing invalid YAML (e.g., - null) and causing Continue to fail to parse the configuration. Filtering out null/undefined elements entirely ensures a clean and valid configuration.

Suggested change
const kept = prior.filter((m) => !m || m.apiBase !== apiBase);
const kept = prior.filter((m) => m && m.apiBase !== apiBase);

});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const body = await res.json();
const list = Array.isArray(body) ? body : body.data ?? body.models ?? [];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If body is null (which can be returned by res.json()), Array.isArray(body) evaluates to false, and accessing body.data will throw a TypeError: Cannot read properties of null (reading 'data'). Using optional chaining body?.data and body?.models prevents this potential crash and adheres to defensive programming practices.

Suggested change
const list = Array.isArray(body) ? body : body.data ?? body.models ?? [];
const list = Array.isArray(body) ? body : (body?.data ?? body?.models ?? []);

Comment on lines +128 to +137
let existing = {};
if (existsSync(configPath)) {
try {
existing = yaml.load(readFileSync(configPath, "utf8")) || {};
} catch {
printInfo("Existing config.yaml unparseable — starting fresh (a .bak is kept).");
if (!dryRun) writeFileSync(`${configPath}.bak`, readFileSync(configPath));
existing = {};
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Reading the file twice (readFileSync inside both try and catch blocks) is redundant. Furthermore, if writeFileSync or the second readFileSync fails (e.g., due to permission issues or a race condition), the error is unhandled and will crash the entire CLI command. Since writing a backup is a best-effort fallback, we should wrap it in a try-catch block and reuse the already-read file content.

Suggested change
let existing = {};
if (existsSync(configPath)) {
try {
existing = yaml.load(readFileSync(configPath, "utf8")) || {};
} catch {
printInfo("Existing config.yaml unparseable — starting fresh (a .bak is kept).");
if (!dryRun) writeFileSync(`${configPath}.bak`, readFileSync(configPath));
existing = {};
}
}
let existing = {};
if (existsSync(configPath)) {
let rawContent;
try {
rawContent = readFileSync(configPath, "utf8");
existing = yaml.load(rawContent) || {};
} catch {
printInfo("Existing config.yaml unparseable — starting fresh (a .bak is kept).");
if (!dryRun && rawContent !== undefined) {
try {
writeFileSync(configPath + ".bak", rawContent, "utf8");
} catch (err) {
printError("Could not write backup file: " + err.message);
}
}
existing = {};
}
}

@diegosouzapw
diegosouzapw merged commit 138ea66 into release/v3.8.30 Jun 19, 2026
4 checks passed
@diegosouzapw
diegosouzapw deleted the feat/continue-cli branch June 19, 2026 16:19
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…Route (diegosouzapw#4289)

CLI diegosouzapw#6 of the series. `omniroute setup-continue` writes Continue's file-based,
mergeable ~/.continue/config.yaml (shared by the VS Code/JetBrains extensions AND
the `cn` CLI) from the live model catalog.

- Each curated model → a Continue model entry: provider: openai, model: <id>,
  apiBase WITH /v1 (Continue appends /chat/completions), apiKey:
  ${{ secrets.OMNIROUTE_API_KEY }} (secret referenced, never written), roles
  [chat, edit, apply] (+ autocomplete for the fast tier).
- Merges into existing config.yaml (js-yaml load/dump): drops prior models on the
  same apiBase, preserves the user's other models + top-level keys.
- Remote-aware (--remote/--api-key → active context → localhost); --only filter.
- Prints how to provide the key (shell env for cn; ~/.continue/.env for IDE).

Researched against current Continue docs: provider: openai + custom apiBase (with
/v1), the ${{ secrets.X }} syntax, roles, and that the `cn` CLI shares the same
config. Continue's wire (/v1/chat/completions) already validated → "OK".

Tests: buildContinueModels (provider/apiBase/secret/roles, fast→autocomplete,
skip uncategorised), mergeContinueConfig (replace-ours/keep-others/defaults),
resolveContinueTarget (/v1). 6 unit tests; check:cli-i18n green.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant