free-coding-models 0.1.36 β†’ 0.1.38

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.
@@ -75,7 +75,7 @@ import chalk from 'chalk'
75
75
  import { createRequire } from 'module'
76
76
  import { readFileSync, writeFileSync, existsSync, copyFileSync, mkdirSync } from 'fs'
77
77
  import { homedir } from 'os'
78
- import { join } from 'path'
78
+ import { join, dirname } from 'path'
79
79
  import { MODELS } from '../sources.js'
80
80
  import { patchOpenClawModelsJson } from '../patch-openclaw-models.js'
81
81
  import { getAvg, getVerdict, getUptime, sortResults, filterByTier, findBestModel, parseArgs, TIER_ORDER, VERDICT_ORDER, TIER_LETTER_MAP } from '../lib/utils.js'
@@ -102,6 +102,7 @@ function runUpdate(latestVersion) {
102
102
  console.log()
103
103
  console.log(chalk.bold.cyan(' ⬆ Updating free-coding-models to v' + latestVersion + '...'))
104
104
  console.log()
105
+
105
106
  try {
106
107
  // πŸ“– Force install from npm registry (ignore local cache)
107
108
  // πŸ“– Use --prefer-online to ensure we get the latest published version
@@ -118,8 +119,37 @@ function runUpdate(latestVersion) {
118
119
  process.exit(0)
119
120
  } catch (err) {
120
121
  console.log()
121
- console.log(chalk.red(' βœ– Update failed. Try manually: npm i -g free-coding-models@' + latestVersion))
122
- console.log()
122
+ // πŸ“– Check if error is permission-related (EACCES or EPERM)
123
+ const isPermissionError = err.code === 'EACCES' || err.code === 'EPERM' ||
124
+ (err.stderr && (err.stderr.includes('EACCES') || err.stderr.includes('permission') ||
125
+ err.stderr.includes('EACCES'))) ||
126
+ (err.message && (err.message.includes('EACCES') || err.message.includes('permission')))
127
+
128
+ if (isPermissionError) {
129
+ console.log(chalk.yellow(' ⚠️ Permission denied. Retrying with sudo...'))
130
+ console.log()
131
+ try {
132
+ execSync(`sudo npm i -g free-coding-models@${latestVersion} --prefer-online`, { stdio: 'inherit' })
133
+ console.log()
134
+ console.log(chalk.green(' βœ… Update complete with sudo! Version ' + latestVersion + ' installed.'))
135
+ console.log()
136
+ console.log(chalk.dim(' πŸ”„ Restarting with new version...'))
137
+ console.log()
138
+
139
+ // πŸ“– Relaunch automatically with the same arguments
140
+ const args = process.argv.slice(2)
141
+ execSync(`node bin/free-coding-models.js ${args.join(' ')}`, { stdio: 'inherit' })
142
+ process.exit(0)
143
+ } catch (sudoErr) {
144
+ console.log()
145
+ console.log(chalk.red(' βœ– Update failed even with sudo. Try manually:'))
146
+ console.log(chalk.dim(' sudo npm i -g free-coding-models@' + latestVersion))
147
+ console.log()
148
+ }
149
+ } else {
150
+ console.log(chalk.red(' βœ– Update failed. Try manually: npm i -g free-coding-models@' + latestVersion))
151
+ console.log()
152
+ }
123
153
  }
124
154
  process.exit(1)
125
155
  }
@@ -660,23 +690,44 @@ async function ping(apiKey, modelId) {
660
690
  }
661
691
 
662
692
  // ─── OpenCode integration ──────────────────────────────────────────────────────
663
- const OPENCODE_CONFIG = join(homedir(), '.config/opencode/opencode.json')
693
+ // πŸ“– Platform-specific config path
694
+ const isWindows = process.platform === 'win32'
695
+ const isMac = process.platform === 'darwin'
696
+ const isLinux = process.platform === 'linux'
697
+
698
+ // πŸ“– OpenCode config location varies by platform
699
+ // πŸ“– Windows: %APPDATA%\opencode\opencode.json (or sometimes ~/.config/opencode)
700
+ // πŸ“– macOS/Linux: ~/.config/opencode/opencode.json
701
+ const OPENCODE_CONFIG = isWindows
702
+ ? join(homedir(), 'AppData', 'Roaming', 'opencode', 'opencode.json')
703
+ : join(homedir(), '.config', 'opencode', 'opencode.json')
704
+
705
+ // πŸ“– Fallback to .config on Windows if AppData doesn't exist
706
+ const OPENCODE_CONFIG_FALLBACK = join(homedir(), '.config', 'opencode', 'opencode.json')
707
+
708
+ function getOpenCodeConfigPath() {
709
+ if (existsSync(OPENCODE_CONFIG)) return OPENCODE_CONFIG
710
+ if (isWindows && existsSync(OPENCODE_CONFIG_FALLBACK)) return OPENCODE_CONFIG_FALLBACK
711
+ return OPENCODE_CONFIG
712
+ }
664
713
 
665
714
  function loadOpenCodeConfig() {
666
- if (!existsSync(OPENCODE_CONFIG)) return { provider: {} }
715
+ const configPath = getOpenCodeConfigPath()
716
+ if (!existsSync(configPath)) return { provider: {} }
667
717
  try {
668
- return JSON.parse(readFileSync(OPENCODE_CONFIG, 'utf8'))
718
+ return JSON.parse(readFileSync(configPath, 'utf8'))
669
719
  } catch {
670
720
  return { provider: {} }
671
721
  }
672
722
  }
673
723
 
674
724
  function saveOpenCodeConfig(config) {
675
- const dir = join(homedir(), '.config/opencode')
725
+ const configPath = getOpenCodeConfigPath()
726
+ const dir = dirname(configPath)
676
727
  if (!existsSync(dir)) {
677
728
  mkdirSync(dir, { recursive: true })
678
729
  }
679
- writeFileSync(OPENCODE_CONFIG, JSON.stringify(config, null, 2))
730
+ writeFileSync(configPath, JSON.stringify(config, null, 2))
680
731
  }
681
732
 
682
733
  // ─── Check NVIDIA NIM in OpenCode config ───────────────────────────────────────
@@ -709,11 +760,11 @@ async function startOpenCode(model) {
709
760
  console.log()
710
761
 
711
762
  const config = loadOpenCodeConfig()
712
- const backupPath = `${OPENCODE_CONFIG}.backup-${Date.now()}`
763
+ const backupPath = `${getOpenCodeConfigPath()}.backup-${Date.now()}`
713
764
 
714
765
  // πŸ“– Backup current config
715
- if (existsSync(OPENCODE_CONFIG)) {
716
- copyFileSync(OPENCODE_CONFIG, backupPath)
766
+ if (existsSync(getOpenCodeConfigPath())) {
767
+ copyFileSync(getOpenCodeConfigPath(), backupPath)
717
768
  console.log(chalk.dim(` πŸ’Ύ Backup: ${backupPath}`))
718
769
  }
719
770
 
@@ -741,7 +792,8 @@ async function startOpenCode(model) {
741
792
  const { spawn } = await import('child_process')
742
793
  const child = spawn('opencode', [], {
743
794
  stdio: 'inherit',
744
- shell: true
795
+ shell: true,
796
+ detached: false
745
797
  })
746
798
 
747
799
  // πŸ“– Wait for OpenCode to exit
@@ -764,7 +816,8 @@ async function startOpenCode(model) {
764
816
  console.log(chalk.dim(' Starting OpenCode with installation prompt…'))
765
817
  console.log()
766
818
 
767
- const installPrompt = `Please install NVIDIA NIM provider in OpenCode by adding this to ~/.config/opencode/opencode.json:
819
+ const configPath = getOpenCodeConfigPath()
820
+ const installPrompt = `Please install NVIDIA NIM provider in OpenCode by adding this to ${configPath}:
768
821
 
769
822
  {
770
823
  "provider": {
@@ -779,7 +832,7 @@ async function startOpenCode(model) {
779
832
  }
780
833
  }
781
834
 
782
- Then set env var: export NVIDIA_API_KEY=your_key_here
835
+ ${isWindows ? 'set NVIDIA_API_KEY=your_key_here' : 'export NVIDIA_API_KEY=your_key_here'}
783
836
 
784
837
  After installation, you can use: opencode --model nvidia/${model.modelId}`
785
838
 
@@ -791,7 +844,8 @@ After installation, you can use: opencode --model nvidia/${model.modelId}`
791
844
  const { spawn } = await import('child_process')
792
845
  const child = spawn('opencode', [], {
793
846
  stdio: 'inherit',
794
- shell: true
847
+ shell: true,
848
+ detached: false
795
849
  })
796
850
 
797
851
  // πŸ“– Wait for OpenCode to exit
@@ -812,7 +866,7 @@ After installation, you can use: opencode --model nvidia/${model.modelId}`
812
866
 
813
867
  // ─── Start OpenCode Desktop ─────────────────────────────────────────────────────
814
868
  // πŸ“– startOpenCodeDesktop: Same config logic as startOpenCode, but opens the Desktop app.
815
- // πŸ“– OpenCode Desktop (/Applications/OpenCode.app) shares config at ~/.config/opencode/opencode.json.
869
+ // πŸ“– OpenCode Desktop shares config at the same location as CLI.
816
870
  // πŸ“– No need to wait for exit β€” Desktop app stays open independently.
817
871
  async function startOpenCodeDesktop(model) {
818
872
  const hasNim = checkNvidiaNimConfig()
@@ -823,10 +877,10 @@ async function startOpenCodeDesktop(model) {
823
877
  console.log()
824
878
 
825
879
  const config = loadOpenCodeConfig()
826
- const backupPath = `${OPENCODE_CONFIG}.backup-${Date.now()}`
880
+ const backupPath = `${getOpenCodeConfigPath()}.backup-${Date.now()}`
827
881
 
828
- if (existsSync(OPENCODE_CONFIG)) {
829
- copyFileSync(OPENCODE_CONFIG, backupPath)
882
+ if (existsSync(getOpenCodeConfigPath())) {
883
+ copyFileSync(getOpenCodeConfigPath(), backupPath)
830
884
  console.log(chalk.dim(` πŸ’Ύ Backup: ${backupPath}`))
831
885
  }
832
886
 
@@ -846,18 +900,43 @@ async function startOpenCodeDesktop(model) {
846
900
  console.log(chalk.dim(' Opening OpenCode Desktop…'))
847
901
  console.log()
848
902
 
849
- // πŸ“– Launch Desktop app β€” no need to wait, it stays open independently
903
+ // πŸ“– Launch Desktop app based on platform
850
904
  const { exec } = await import('child_process')
851
- exec('open -a OpenCode', (err) => {
905
+
906
+ let command
907
+ if (isMac) {
908
+ command = 'open -a OpenCode'
909
+ } else if (isWindows) {
910
+ // πŸ“– On Windows, try common installation paths
911
+ // πŸ“– User installation: %LOCALAPPDATA%\Programs\OpenCode\OpenCode.exe
912
+ // πŸ“– System installation: C:\Program Files\OpenCode\OpenCode.exe
913
+ command = 'start "" "%LOCALAPPDATA%\\Programs\\OpenCode\\OpenCode.exe" 2>nul || start "" "%PROGRAMFILES%\\OpenCode\\OpenCode.exe" 2>nul || start OpenCode'
914
+ } else if (isLinux) {
915
+ // πŸ“– On Linux, try different methods
916
+ // πŸ“– Check if opencode-desktop exists, otherwise try xdg-open
917
+ command = 'opencode-desktop 2>/dev/null || xdg-open /usr/share/applications/opencode.desktop 2>/dev/null || flatpak run ai.opencode.OpenCode 2>/dev/null || snap run opencode 2>/dev/null || echo "OpenCode not found"'
918
+ }
919
+
920
+ exec(command, (err, stdout, stderr) => {
852
921
  if (err) {
853
- console.error(chalk.red(' βœ— Could not open OpenCode Desktop β€” is it installed at /Applications/OpenCode.app?'))
922
+ console.error(chalk.red(' βœ— Could not open OpenCode Desktop'))
923
+ if (isWindows) {
924
+ console.error(chalk.dim(' Make sure OpenCode is installed from https://opencode.ai'))
925
+ } else if (isLinux) {
926
+ console.error(chalk.dim(' Install via: snap install opencode OR flatpak install ai.opencode.OpenCode'))
927
+ console.error(chalk.dim(' Or download from https://opencode.ai'))
928
+ } else {
929
+ console.error(chalk.dim(' Is it installed at /Applications/OpenCode.app?'))
930
+ }
854
931
  }
855
932
  })
856
933
  } else {
857
934
  console.log(chalk.yellow(' ⚠ NVIDIA NIM not configured in OpenCode'))
858
935
  console.log(chalk.dim(' Please configure it first. Config is shared between CLI and Desktop.'))
859
936
  console.log()
860
- const installPrompt = `Add this to ~/.config/opencode/opencode.json:
937
+
938
+ const configPath = getOpenCodeConfigPath()
939
+ const installPrompt = `Add this to ${configPath}:
861
940
 
862
941
  {
863
942
  "provider": {
@@ -872,7 +951,7 @@ async function startOpenCodeDesktop(model) {
872
951
  }
873
952
  }
874
953
 
875
- Then set env var: export NVIDIA_API_KEY=your_key_here`
954
+ ${isWindows ? 'set NVIDIA_API_KEY=your_key_here' : 'export NVIDIA_API_KEY=your_key_here'}`
876
955
  console.log(chalk.cyan(installPrompt))
877
956
  console.log()
878
957
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "free-coding-models",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
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",