free-coding-models 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  <img src="https://img.shields.io/npm/v/free-coding-models?color=76b900&label=npm&logo=npm" alt="npm version">
3
3
  <img src="https://img.shields.io/node/v/free-coding-models?color=76b900&logo=node.js" alt="node version">
4
4
  <img src="https://img.shields.io/npm/l/free-coding-models?color=76b900" alt="license">
5
- <img src="https://img.shields.io/badge/models-44-76b900?logo=nvidia" alt="models count">
5
+ <img src="https://img.shields.io/badge/nvidia%20nim%20models-44-76b900?logo=nvidia" alt="models count">
6
6
  </p>
7
7
 
8
8
  <h1 align="center">free-coding-models</h1>
@@ -100,9 +100,12 @@ bunx free-coding-models YOUR_API_KEY
100
100
  # Just run it — shows a startup menu to pick OpenCode or OpenClaw, prompts for API key if not set
101
101
  free-coding-models
102
102
 
103
- # Explicitly target OpenCode (current default behavior — TUI + Enter launches OpenCode)
103
+ # Explicitly target OpenCode CLI (TUI + Enter launches OpenCode CLI)
104
104
  free-coding-models --opencode
105
105
 
106
+ # Explicitly target OpenCode Desktop (TUI + Enter sets model & opens Desktop app)
107
+ free-coding-models --opencode-desktop
108
+
106
109
  # Explicitly target OpenClaw (TUI + Enter sets model as default in OpenClaw)
107
110
  free-coding-models --openclaw
108
111
 
@@ -130,8 +133,11 @@ When you run `free-coding-models` without `--opencode` or `--openclaw`, you get
130
133
  ```
131
134
  ⚡ Free Coding Models — Choose your tool
132
135
 
133
- ❯ 💻 OpenCode
134
- Press Enter on a model → launch OpenCode with it as default
136
+ ❯ 💻 OpenCode CLI
137
+ Press Enter on a model → launch OpenCode CLI with it as default
138
+
139
+ 🖥 OpenCode Desktop
140
+ Press Enter on a model → set model & open OpenCode Desktop app
135
141
 
136
142
  🦞 OpenClaw
137
143
  Press Enter on a model → set it as default in OpenClaw config
@@ -417,7 +423,8 @@ This script:
417
423
  | Flag | Description |
418
424
  |------|-------------|
419
425
  | *(none)* | Show startup menu to choose OpenCode or OpenClaw |
420
- | `--opencode` | OpenCode mode — Enter launches OpenCode with selected model |
426
+ | `--opencode` | OpenCode CLI mode — Enter launches OpenCode CLI with selected model |
427
+ | `--opencode-desktop` | OpenCode Desktop mode — Enter sets model & opens OpenCode Desktop app |
421
428
  | `--openclaw` | OpenClaw mode — Enter sets selected model as default in OpenClaw |
422
429
  | `--best` | Show only top-tier models (A+, S, S+) |
423
430
  | `--fiable` | Analyze 10 seconds, output the most reliable model as `provider/model_id` |
@@ -16,7 +16,7 @@
16
16
  * - Best-per-tier highlighting with medals (🥇🥈🥉)
17
17
  * - Interactive navigation with arrow keys directly in the table
18
18
  * - Instant OpenCode OR OpenClaw action on Enter key press
19
- * - Startup mode menu (OpenCode vs OpenClaw) when no flag is given
19
+ * - Startup mode menu (OpenCode CLI vs OpenCode Desktop vs OpenClaw) when no flag is given
20
20
  * - Automatic config detection and model setup for both tools
21
21
  * - Persistent API key storage in ~/.free-coding-models
22
22
  * - Multi-source support via sources.js (easily add new providers)
@@ -35,7 +35,8 @@
35
35
  * - `getUptime`: Calculate uptime percentage from ping history
36
36
  * - `sortResults`: Sort models by various columns
37
37
  * - `checkNvidiaNimConfig`: Check if NVIDIA NIM provider is configured in OpenCode
38
- * - `startOpenCode`: Launch OpenCode with selected model (configures if needed)
38
+ * - `startOpenCode`: Launch OpenCode CLI with selected model (configures if needed)
39
+ * - `startOpenCodeDesktop`: Set model in shared config & open OpenCode Desktop app
39
40
  * - `loadOpenClawConfig` / `saveOpenClawConfig`: Manage ~/.openclaw/openclaw.json
40
41
  * - `startOpenClaw`: Set selected model as default in OpenClaw config (remote, no launch)
41
42
  * - `filterByTier`: Filter models by tier letter prefix (S, A, B, C)
@@ -58,7 +59,8 @@
58
59
  *
59
60
  * 🚀 CLI flags:
60
61
  * - (no flag): Show startup menu → choose OpenCode or OpenClaw
61
- * - --opencode: OpenCode mode (launch with selected model)
62
+ * - --opencode: OpenCode CLI mode (launch CLI with selected model)
63
+ * - --opencode-desktop: OpenCode Desktop mode (set model & open Desktop app)
62
64
  * - --openclaw: OpenClaw mode (set selected model as default in OpenClaw)
63
65
  * - --best: Show only top-tier models (A+, S, S+)
64
66
  * - --fiable: Analyze 10s and output the most reliable model
@@ -166,9 +168,14 @@ async function promptApiKey() {
166
168
  async function promptModeSelection(latestVersion) {
167
169
  const options = [
168
170
  {
169
- label: 'OpenCode',
171
+ label: 'OpenCode CLI',
170
172
  icon: '💻',
171
- description: 'Press Enter on a model → launch OpenCode with it as default',
173
+ description: 'Press Enter on a model → launch OpenCode CLI with it as default',
174
+ },
175
+ {
176
+ label: 'OpenCode Desktop',
177
+ icon: '🖥',
178
+ description: 'Press Enter on a model → set model & open OpenCode Desktop app',
172
179
  },
173
180
  {
174
181
  label: 'OpenClaw',
@@ -185,6 +192,15 @@ async function promptModeSelection(latestVersion) {
185
192
  })
186
193
  }
187
194
 
195
+ // 📖 Add "Read Changelogs" option when an update is available or was just updated
196
+ if (latestVersion) {
197
+ options.push({
198
+ label: 'Read Changelogs',
199
+ icon: '📋',
200
+ description: 'Open the GitHub releases page in your browser',
201
+ })
202
+ }
203
+
188
204
  return new Promise((resolve) => {
189
205
  let selected = 0
190
206
 
@@ -237,8 +253,9 @@ async function promptModeSelection(latestVersion) {
237
253
  if (process.stdin.isTTY) process.stdin.setRawMode(false)
238
254
  process.stdin.removeListener('keypress', onKey)
239
255
  process.stdin.pause()
240
- const choices = ['opencode', 'openclaw']
256
+ const choices = ['opencode', 'opencode-desktop', 'openclaw']
241
257
  if (latestVersion) choices.push('update')
258
+ if (latestVersion) choices.push('changelogs')
242
259
  resolve(choices[selected])
243
260
  }
244
261
  }
@@ -324,9 +341,14 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
324
341
  : chalk.dim(`next ping ${secondsUntilNext}s`)
325
342
 
326
343
  // 📖 Mode badge shown in header so user knows what Enter will do
327
- const modeBadge = mode === 'openclaw'
328
- ? chalk.bold.rgb(255, 100, 50)(' [🦞 OpenClaw]')
329
- : chalk.bold.rgb(0, 200, 255)(' [💻 OpenCode]')
344
+ let modeBadge
345
+ if (mode === 'openclaw') {
346
+ modeBadge = chalk.bold.rgb(255, 100, 50)(' [🦞 OpenClaw]')
347
+ } else if (mode === 'opencode-desktop') {
348
+ modeBadge = chalk.bold.rgb(0, 200, 255)(' [🖥 Desktop]')
349
+ } else {
350
+ modeBadge = chalk.bold.rgb(0, 200, 255)(' [💻 CLI]')
351
+ }
330
352
 
331
353
  // 📖 Column widths (generous spacing with margins)
332
354
  const W_RANK = 6
@@ -527,7 +549,9 @@ function renderTable(results, pendingPings, frame, cursor = null, sortColumn = '
527
549
  // 📖 Footer hints adapt based on active mode
528
550
  const actionHint = mode === 'openclaw'
529
551
  ? chalk.rgb(255, 100, 50)('Enter→SetOpenClaw')
530
- : chalk.rgb(0, 200, 255)('Enter→OpenCode')
552
+ : mode === 'opencode-desktop'
553
+ ? chalk.rgb(0, 200, 255)('Enter→OpenDesktop')
554
+ : chalk.rgb(0, 200, 255)('Enter→OpenCode')
531
555
  lines.push(chalk.dim(` ↑↓ Navigate • `) + actionHint + chalk.dim(` • R/T/O/M/P/A/S/V/U Sort • W↓/X↑ Interval (${intervalSec}s) • Ctrl+C Exit`))
532
556
  lines.push('')
533
557
  lines.push(chalk.dim(' made with ') + '🩷' + chalk.dim(' by vava-nessa • ') + chalk.dim.underline('https://github.com/vava-nessa/free-coding-models'))
@@ -710,6 +734,74 @@ After installation, you can use: opencode --model nvidia/${model.modelId}`
710
734
  }
711
735
  }
712
736
 
737
+ // ─── Start OpenCode Desktop ─────────────────────────────────────────────────────
738
+ // 📖 startOpenCodeDesktop: Same config logic as startOpenCode, but opens the Desktop app.
739
+ // 📖 OpenCode Desktop (/Applications/OpenCode.app) shares config at ~/.config/opencode/opencode.json.
740
+ // 📖 No need to wait for exit — Desktop app stays open independently.
741
+ async function startOpenCodeDesktop(model) {
742
+ const hasNim = checkNvidiaNimConfig()
743
+
744
+ if (hasNim) {
745
+ console.log(chalk.green(` 🖥 Setting ${chalk.bold(model.label)} as default for OpenCode Desktop…`))
746
+ console.log(chalk.dim(` Model: nvidia/${model.modelId}`))
747
+ console.log()
748
+
749
+ const config = loadOpenCodeConfig()
750
+ const backupPath = `${OPENCODE_CONFIG}.backup-${Date.now()}`
751
+
752
+ if (existsSync(OPENCODE_CONFIG)) {
753
+ copyFileSync(OPENCODE_CONFIG, backupPath)
754
+ console.log(chalk.dim(` 💾 Backup: ${backupPath}`))
755
+ }
756
+
757
+ config.model = `nvidia/${model.modelId}`
758
+
759
+ if (config.provider?.nvidia) {
760
+ if (!config.provider.nvidia.models) config.provider.nvidia.models = {}
761
+ config.provider.nvidia.models[model.modelId] = {
762
+ name: model.label,
763
+ }
764
+ }
765
+
766
+ saveOpenCodeConfig(config)
767
+
768
+ console.log(chalk.green(` ✓ Default model set to: nvidia/${model.modelId}`))
769
+ console.log()
770
+ console.log(chalk.dim(' Opening OpenCode Desktop…'))
771
+ console.log()
772
+
773
+ // 📖 Launch Desktop app — no need to wait, it stays open independently
774
+ const { exec } = await import('child_process')
775
+ exec('open -a OpenCode', (err) => {
776
+ if (err) {
777
+ console.error(chalk.red(' ✗ Could not open OpenCode Desktop — is it installed at /Applications/OpenCode.app?'))
778
+ }
779
+ })
780
+ } else {
781
+ console.log(chalk.yellow(' ⚠ NVIDIA NIM not configured in OpenCode'))
782
+ console.log(chalk.dim(' Please configure it first. Config is shared between CLI and Desktop.'))
783
+ console.log()
784
+ const installPrompt = `Add this to ~/.config/opencode/opencode.json:
785
+
786
+ {
787
+ "provider": {
788
+ "nvidia": {
789
+ "npm": "@ai-sdk/openai-compatible",
790
+ "name": "NVIDIA NIM",
791
+ "options": {
792
+ "baseURL": "https://integrate.api.nvidia.com/v1",
793
+ "apiKey": "{env:NVIDIA_API_KEY}"
794
+ }
795
+ }
796
+ }
797
+ }
798
+
799
+ Then set env var: export NVIDIA_API_KEY=your_key_here`
800
+ console.log(chalk.cyan(installPrompt))
801
+ console.log()
802
+ }
803
+ }
804
+
713
805
  // ─── OpenClaw integration ──────────────────────────────────────────────────────
714
806
  // 📖 OpenClaw config: ~/.openclaw/openclaw.json (JSON format, may be JSON5 in newer versions)
715
807
  // 📖 To set a model: set agents.defaults.model.primary = "nvidia/model-id"
@@ -892,7 +984,7 @@ async function main() {
892
984
  // 📖 Parse CLI arguments using shared parseArgs utility
893
985
  const parsed = parseArgs(process.argv)
894
986
  let apiKey = parsed.apiKey
895
- const { bestMode, fiableMode, openCodeMode, openClawMode, tierFilter } = parsed
987
+ const { bestMode, fiableMode, openCodeMode, openCodeDesktopMode, openClawMode, tierFilter } = parsed
896
988
 
897
989
  // 📖 Priority: CLI arg > env var > saved config > wizard
898
990
  if (!apiKey) {
@@ -925,6 +1017,8 @@ async function main() {
925
1017
  let mode
926
1018
  if (openClawMode) {
927
1019
  mode = 'openclaw'
1020
+ } else if (openCodeDesktopMode) {
1021
+ mode = 'opencode-desktop'
928
1022
  } else if (openCodeMode) {
929
1023
  mode = 'opencode'
930
1024
  } else {
@@ -937,8 +1031,16 @@ async function main() {
937
1031
  runUpdate()
938
1032
  }
939
1033
 
1034
+ // 📖 Handle "Read Changelogs" selection — open GitHub releases in browser
1035
+ if (mode === 'changelogs') {
1036
+ const { exec } = await import('child_process')
1037
+ exec('open https://github.com/vava-nessa/free-coding-models/releases')
1038
+ console.log(chalk.dim(' 📋 Opening changelogs in browser…'))
1039
+ process.exit(0)
1040
+ }
1041
+
940
1042
  // 📖 When using flags (--opencode/--openclaw), show update warning in terminal
941
- if (latestVersion && (openCodeMode || openClawMode)) {
1043
+ if (latestVersion && (openCodeMode || openCodeDesktopMode || openClawMode)) {
942
1044
  console.log(chalk.red(` ⚠ New version available (v${latestVersion}), please run npm i -g free-coding-models to install`))
943
1045
  console.log()
944
1046
  }
@@ -1079,6 +1181,8 @@ async function main() {
1079
1181
  // 📖 Dispatch to the correct integration based on active mode
1080
1182
  if (state.mode === 'openclaw') {
1081
1183
  await startOpenClaw(userSelected, apiKey)
1184
+ } else if (state.mode === 'opencode-desktop') {
1185
+ await startOpenCodeDesktop(userSelected)
1082
1186
  } else {
1083
1187
  await startOpenCode(userSelected)
1084
1188
  }
package/lib/utils.js CHANGED
@@ -242,11 +242,11 @@ export function findBestModel(results) {
242
242
  //
243
243
  // 📖 Argument types:
244
244
  // - API key: first positional arg that doesn't start with "--" (e.g., "nvapi-xxx")
245
- // - Boolean flags: --best, --fiable, --opencode, --openclaw (case-insensitive)
245
+ // - Boolean flags: --best, --fiable, --opencode, --opencode-desktop, --openclaw (case-insensitive)
246
246
  // - Value flag: --tier <letter> (the next non-flag arg is the tier value)
247
247
  //
248
248
  // 📖 Returns:
249
- // { apiKey, bestMode, fiableMode, openCodeMode, openClawMode, tierFilter }
249
+ // { apiKey, bestMode, fiableMode, openCodeMode, openCodeDesktopMode, openClawMode, tierFilter }
250
250
  //
251
251
  // 📖 Note: apiKey may be null here — the main CLI falls back to env vars and saved config.
252
252
  export function parseArgs(argv) {
@@ -265,6 +265,7 @@ export function parseArgs(argv) {
265
265
  const bestMode = flags.includes('--best')
266
266
  const fiableMode = flags.includes('--fiable')
267
267
  const openCodeMode = flags.includes('--opencode')
268
+ const openCodeDesktopMode = flags.includes('--opencode-desktop')
268
269
  const openClawMode = flags.includes('--openclaw')
269
270
 
270
271
  // 📖 --tier requires a value after it (e.g., --tier S)
@@ -275,5 +276,5 @@ export function parseArgs(argv) {
275
276
  tierFilter = args[tierIdx + 1].toUpperCase()
276
277
  }
277
278
 
278
- return { apiKey, bestMode, fiableMode, openCodeMode, openClawMode, tierFilter }
279
+ return { apiKey, bestMode, fiableMode, openCodeMode, openCodeDesktopMode, openClawMode, tierFilter }
279
280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Find the fastest coding LLM models in seconds — ping free models from multiple providers, pick the best one for OpenCode, Cursor, or any AI coding assistant.",
5
5
  "keywords": [
6
6
  "nvidia",